[hibernate-commits] Hibernate SVN: r19034 - in validator/trunk/hibernate-validator/src: main/java/org/hibernate/validator/engine and 6 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Sun Mar 21 18:08:11 EDT 2010


Author: hardy.ferentschik
Date: 2010-03-21 18:08:06 -0400 (Sun, 21 Mar 2010)
New Revision: 19034

Added:
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/Constants.java
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/AggregateResourceBundleLocator.java
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/CachingResourceBundleLocator.java
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/DelegatingResourceBundleLocator.java
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/PlatformResourceBundleLocator.java
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/ResourceBundleLocator.java
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/package.html
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/util/IteratorEnumeration.java
   validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/HibernateValidatorConfigurationTest.java
   validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/resourceloading/
   validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/resourceloading/AggregateBundleTest.java
   validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/resourceloading/AggregateResourceBundleLocatorTest.java
   validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/resourceloading/AggregateResourceBundleLocatorTestBundle1.properties
   validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/resourceloading/AggregateResourceBundleLocatorTestBundle2.properties
Modified:
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/ConfigurationImpl.java
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/ResourceBundleMessageInterpolator.java
   validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/messageinterpolation/MessageInterpolationTest.java
   validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/messageinterpolation/ResourceBundleMessageInterpolatorTest.java
Log:
 HV-238 Commited Gunnar's patch for this issue. Just made some formatting changes.

Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java	2010-03-19 21:27:15 UTC (rev 19033)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/HibernateValidatorConfiguration.java	2010-03-21 22:08:06 UTC (rev 19034)
@@ -19,11 +19,48 @@
 
 import javax.validation.Configuration;
 
+import org.hibernate.validator.engine.ResourceBundleMessageInterpolator;
+import org.hibernate.validator.engine.resourceloading.ResourceBundleLocator;
+
 /**
- * Uniquely identify Hibernate Validator in the Bean Validation bootstrap strategy
- * Also contains Hibernate Validator specific configurations
- * 
+ * Uniquely identifies Hibernate Validator in the Bean Validation bootstrap
+ * strategy. Also contains Hibernate Validator specific configurations.
+ *
  * @author Emmanuel Bernard
+ * @author Gunnar Morling
  */
 public interface HibernateValidatorConfiguration extends Configuration<HibernateValidatorConfiguration> {
+
+	/**
+	 * <p>
+	 * Returns the {@link ResourceBundleLocator} used by the
+	 * {@link Configuration#getDefaultMessageInterpolator() default message
+	 * interpolator} to load user-provided resource bundles. In conformance with
+	 * the specification this default locator retrieves the bundle
+	 * "ValidationMessages".
+	 * </p>
+	 * <p>
+	 * This locator can be used as delegate for custom locators when setting a
+	 * customized {@link ResourceBundleMessageInterpolator}:
+	 * </p>
+	 * <p/>
+	 * <pre>
+	 * {@code
+	 * 	HibernateValidatorConfiguration configure =
+	 *    Validation.byProvider(HibernateValidator.class).configure();
+	 * <p/>
+	 *  ResourceBundleLocator defaultResourceBundleLocator =
+	 *    configure.getDefaultResourceBundleLocator();
+	 *  ResourceBundleLocator myResourceBundleLocator =
+	 *    new MyResourceBundleLocator(defaultResourceBundleLocator);
+	 * <p/>
+	 *  configure.messageInterpolator(
+	 *    new ResourceBundleMessageInterpolator(myResourceBundleLocator));
+	 * }
+	 * </pre>
+	 *
+	 * @return The default {@link ResourceBundleLocator}. Never null.
+	 */
+	ResourceBundleLocator getDefaultResourceBundleLocator();
+
 }

Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/ConfigurationImpl.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/ConfigurationImpl.java	2010-03-19 21:27:15 UTC (rev 19033)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/ConfigurationImpl.java	2010-03-21 22:08:06 UTC (rev 19034)
@@ -1,7 +1,7 @@
 // $Id$
 /*
 * JBoss, Home of Professional Open Source
-* Copyright 2009, Red Hat, Inc. and/or its affiliates, and individual contributors
+* Copyright 2010, Red Hat, Inc. and/or its affiliates, and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
@@ -33,18 +33,21 @@
 
 import org.slf4j.Logger;
 
+import org.hibernate.validator.HibernateValidatorConfiguration;
 import org.hibernate.validator.engine.resolver.DefaultTraversableResolver;
+import org.hibernate.validator.engine.resourceloading.PlatformResourceBundleLocator;
+import org.hibernate.validator.engine.resourceloading.ResourceBundleLocator;
 import org.hibernate.validator.util.LoggerFactory;
 import org.hibernate.validator.util.Version;
 import org.hibernate.validator.xml.ValidationBootstrapParameters;
 import org.hibernate.validator.xml.ValidationXmlParser;
-import org.hibernate.validator.HibernateValidatorConfiguration;
 
 /**
  * Hibernate specific <code>Configuration</code> implementation.
  *
  * @author Emmanuel Bernard
  * @author Hardy Ferentschik
+ * @author Gunnar Morling
  */
 public class ConfigurationImpl implements HibernateValidatorConfiguration, ConfigurationState {
 
@@ -54,7 +57,10 @@
 
 	private static final Logger log = LoggerFactory.make();
 
-	private final MessageInterpolator defaultMessageInterpolator = new ResourceBundleMessageInterpolator();
+	private final ResourceBundleLocator defaultResourceBundleLocator = new PlatformResourceBundleLocator( Constants.USER_VALIDATION_MESSAGES );
+	private final MessageInterpolator defaultMessageInterpolator = new ResourceBundleMessageInterpolator(
+			defaultResourceBundleLocator
+	);
 	private final TraversableResolver defaultTraversableResolver = new DefaultTraversableResolver();
 	private final ConstraintValidatorFactory defaultConstraintValidatorFactory = new ConstraintValidatorFactoryImpl();
 	private final ValidationProviderResolver providerResolver;
@@ -180,6 +186,10 @@
 		return defaultConstraintValidatorFactory;
 	}
 
+	public ResourceBundleLocator getDefaultResourceBundleLocator() {
+		return defaultResourceBundleLocator;
+	}
+
 	private boolean isSpecificProvider() {
 		return validationBootstrapParameters.provider != null;
 	}

Added: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/Constants.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/Constants.java	                        (rev 0)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/Constants.java	2010-03-21 22:08:06 UTC (rev 19034)
@@ -0,0 +1,40 @@
+/*
+ * $Id:$
+ *
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat, Inc. and/or its affiliates, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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.hibernate.validator.engine;
+
+/**
+ * Constants used throughout the Hibernate Validator project.
+ *
+ * @author Gunnar Morling
+ */
+public final class Constants {
+
+	private Constants() {
+	}
+
+	/**
+	 * The name of the default message bundle.
+	 */
+	public static final String DEFAULT_VALIDATION_MESSAGES = "org.hibernate.validator.ValidationMessages";
+
+	/**
+	 * The name of the user-provided message bundle as defined in the specification.
+	 */
+	public static final String USER_VALIDATION_MESSAGES = "ValidationMessages";
+}

Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/ResourceBundleMessageInterpolator.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/ResourceBundleMessageInterpolator.java	2010-03-19 21:27:15 UTC (rev 19033)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/ResourceBundleMessageInterpolator.java	2010-03-21 22:08:06 UTC (rev 19034)
@@ -17,32 +17,27 @@
 */
 package org.hibernate.validator.engine;
 
-import java.security.AccessController;
 import java.util.Locale;
 import java.util.Map;
 import java.util.MissingResourceException;
 import java.util.ResourceBundle;
 import java.util.WeakHashMap;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import javax.validation.MessageInterpolator;
 
-import org.slf4j.Logger;
+import org.hibernate.validator.engine.resourceloading.CachingResourceBundleLocator;
+import org.hibernate.validator.engine.resourceloading.PlatformResourceBundleLocator;
+import org.hibernate.validator.engine.resourceloading.ResourceBundleLocator;
 
-import org.hibernate.validator.util.GetClassLoader;
-import org.hibernate.validator.util.LoggerFactory;
-
 /**
  * Resource bundle backed message interpolator.
  *
  * @author Emmanuel Bernard
  * @author Hardy Ferentschik
+ * @author Gunnar Morling
  */
 public class ResourceBundleMessageInterpolator implements MessageInterpolator {
-	private static final String DEFAULT_VALIDATION_MESSAGES = "org.hibernate.validator.ValidationMessages";
-	private static final String USER_VALIDATION_MESSAGES = "ValidationMessages";
-	private static final Logger log = LoggerFactory.make();
 
 	/**
 	 * Regular expression used to do message interpolation.
@@ -55,14 +50,14 @@
 	private final Locale defaultLocale;
 
 	/**
-	 * User specified resource bundles hashed against their locale.
+	 * Loads user-specified resource bundles.
 	 */
-	private final Map<Locale, ResourceBundle> userBundlesMap = new ConcurrentHashMap<Locale, ResourceBundle>();
+	private final ResourceBundleLocator userResourceBundleLocator;
 
 	/**
-	 * Built-in resource bundles hashed against there locale.
+	 * Loads built-in resource bundles.
 	 */
-	private final Map<Locale, ResourceBundle> defaultBundlesMap = new ConcurrentHashMap<Locale, ResourceBundle>();
+	private final ResourceBundleLocator defaultResourceBundleLocator;
 
 	/**
 	 * Step 1-3 of message interpolation can be cached. We do this in this map.
@@ -70,25 +65,40 @@
 	private final Map<LocalisedMessage, String> resolvedMessages = new WeakHashMap<LocalisedMessage, String>();
 
 	public ResourceBundleMessageInterpolator() {
-		this( null );
+		this( ( ResourceBundleLocator ) null );
 	}
 
-	public ResourceBundleMessageInterpolator(ResourceBundle resourceBundle) {
+	/**
+	 * @param resourceBundle the resource bundle to use
+	 *
+	 * @deprecated Use {@link ResourceBundleMessageInterpolator#ResourceBundleMessageInterpolator(ResourceBundleLocator)} instead.
+	 */
+	// TODO GM: Do we still need this? It was only used in tests, but there might be 3rd party code using it.
+	@Deprecated
+	public ResourceBundleMessageInterpolator(final ResourceBundle resourceBundle) {
+		this(
+				new PlatformResourceBundleLocator( Constants.USER_VALIDATION_MESSAGES ) {
+					public ResourceBundle getResourceBundle(Locale locale) {
+						return locale == Locale.getDefault() ? resourceBundle : super.getResourceBundle( locale );
+					}
+				}
+		);
+	}
 
+	public ResourceBundleMessageInterpolator(ResourceBundleLocator userResourceBundleLocator) {
+
 		defaultLocale = Locale.getDefault();
 
-		if ( resourceBundle == null ) {
-			ResourceBundle bundle = getFileBasedResourceBundle( defaultLocale );
-			if ( bundle != null ) {
-				userBundlesMap.put( defaultLocale, bundle );
-			}
-
+		if ( userResourceBundleLocator == null ) {
+			userResourceBundleLocator = new PlatformResourceBundleLocator( Constants.USER_VALIDATION_MESSAGES );
 		}
-		else {
-			userBundlesMap.put( defaultLocale, resourceBundle );
-		}
 
-		defaultBundlesMap.put( defaultLocale, ResourceBundle.getBundle( DEFAULT_VALIDATION_MESSAGES, defaultLocale ) );
+		this.userResourceBundleLocator = new CachingResourceBundleLocator( userResourceBundleLocator );
+
+		this.defaultResourceBundleLocator =
+				new CachingResourceBundleLocator(
+						new PlatformResourceBundleLocator( Constants.DEFAULT_VALIDATION_MESSAGES )
+				);
 	}
 
 	public String interpolate(String message, Context context) {
@@ -120,8 +130,10 @@
 
 		// if the message is not already in the cache we have to run step 1-3 of the message resolution 
 		if ( resolvedMessage == null ) {
-			ResourceBundle userResourceBundle = findUserResourceBundle( locale );
-			ResourceBundle defaultResourceBundle = findDefaultResourceBundle( locale );
+			ResourceBundle userResourceBundle = userResourceBundleLocator
+					.getResourceBundle( locale );
+			ResourceBundle defaultResourceBundle = defaultResourceBundleLocator
+					.getResourceBundle( locale );
 
 			String userBundleResolvedMessage;
 			resolvedMessage = message;
@@ -160,53 +172,6 @@
 		return !origMessage.equals( newMessage );
 	}
 
-	/**
-	 * Search current thread classloader for the resource bundle. If not found, search validator (this) classloader.
-	 *
-	 * @param locale The locale of the bundle to load.
-	 *
-	 * @return the resource bundle or <code>null</code> if none is found.
-	 */
-	private ResourceBundle getFileBasedResourceBundle(Locale locale) {
-		ResourceBundle rb = null;
-		boolean isSecured = System.getSecurityManager() != null;
-		GetClassLoader action = GetClassLoader.fromContext();
-		ClassLoader classLoader = isSecured ? AccessController.doPrivileged( action ) : action.run();
-
-		if ( classLoader != null ) {
-			rb = loadBundle( classLoader, locale, USER_VALIDATION_MESSAGES + " not found by thread local classloader" );
-		}
-		if ( rb == null ) {
-			action = GetClassLoader.fromClass( ResourceBundleMessageInterpolator.class );
-			classLoader = isSecured ? AccessController.doPrivileged( action ) : action.run();
-			rb = loadBundle(
-					classLoader,
-					locale,
-					USER_VALIDATION_MESSAGES + " not found by validator classloader"
-			);
-		}
-		if ( log.isDebugEnabled() ) {
-			if ( rb != null ) {
-				log.debug( USER_VALIDATION_MESSAGES + " found" );
-			}
-			else {
-				log.debug( USER_VALIDATION_MESSAGES + " not found. Delegating to " + DEFAULT_VALIDATION_MESSAGES );
-			}
-		}
-		return rb;
-	}
-
-	private ResourceBundle loadBundle(ClassLoader classLoader, Locale locale, String message) {
-		ResourceBundle rb = null;
-		try {
-			rb = ResourceBundle.getBundle( USER_VALIDATION_MESSAGES, locale, classLoader );
-		}
-		catch ( MissingResourceException e ) {
-			log.trace( message );
-		}
-		return rb;
-	}
-
 	private String replaceVariables(String message, ResourceBundle bundle, Locale locale, boolean recurse) {
 		Matcher matcher = messageParameterPattern.matcher( message );
 		StringBuffer sb = new StringBuffer();
@@ -266,28 +231,6 @@
 		return parameter.substring( 1, parameter.length() - 1 );
 	}
 
-	private ResourceBundle findDefaultResourceBundle(Locale locale) {
-		if ( defaultBundlesMap.containsKey( locale ) ) {
-			return defaultBundlesMap.get( locale );
-		}
-
-		ResourceBundle bundle = ResourceBundle.getBundle( DEFAULT_VALIDATION_MESSAGES, locale );
-		defaultBundlesMap.put( locale, bundle );
-		return bundle;
-	}
-
-	private ResourceBundle findUserResourceBundle(Locale locale) {
-		if ( userBundlesMap.containsKey( locale ) ) {
-			return userBundlesMap.get( locale );
-		}
-
-		ResourceBundle bundle = getFileBasedResourceBundle( locale );
-		if ( bundle != null ) {
-			userBundlesMap.put( locale, bundle );
-		}
-		return bundle;
-	}
-
 	/**
 	 * @param s The string in which to replace the meta characters '$' and '\'.
 	 *
@@ -308,14 +251,6 @@
 			this.locale = locale;
 		}
 
-		public String getMessage() {
-			return message;
-		}
-
-		public Locale getLocale() {
-			return locale;
-		}
-
 		@Override
 		public boolean equals(Object o) {
 			if ( this == o ) {

Added: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/AggregateResourceBundleLocator.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/AggregateResourceBundleLocator.java	                        (rev 0)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/AggregateResourceBundleLocator.java	2010-03-21 22:08:06 UTC (rev 19034)
@@ -0,0 +1,147 @@
+// $Id: AggregateResourceBundleLocator.java 17620 2009-10-04 19:19:28Z hardy.ferentschik $
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2010, Red Hat, Inc. and/or its affiliates, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* 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.hibernate.validator.engine.resourceloading;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import org.hibernate.validator.util.IteratorEnumeration;
+
+/**
+ * A {@link ResourceBundleLocator} implementation that provides access
+ * to multiple source {@link ResourceBundle}s by merging them into one
+ * aggregated bundle.
+ *
+ * @author Gunnar Morling
+ */
+public class AggregateResourceBundleLocator extends DelegatingResourceBundleLocator {
+
+	private final List<String> bundleNames;
+
+	/**
+	 * Creates a locator that delivers a resource bundle merged from the given
+	 * list of source bundles.
+	 *
+	 * @param bundleNames A list with source bundle names. The returned bundle will
+	 * contain all entries from all source bundles. In case a key occurs
+	 * in multiple source bundles, the value will be taken from the
+	 * first bundle containing the key.
+	 */
+	public AggregateResourceBundleLocator(List<String> bundleNames) {
+		this( bundleNames, null );
+	}
+
+	/**
+	 * Creates a locator that delivers a resource bundle merged from the given
+	 * list of source bundles.
+	 *
+	 * @param bundleNames A list with source bundle names. The returned bundle will
+	 * contain all keys from all source bundles. In case a key occurs
+	 * in multiple source bundles, the value will be taken from the
+	 * first bundle containing the key.
+	 * @param delegate A delegate resource bundle locator. The bundle returned by
+	 * this locator will be added to the aggregate bundle after all
+	 * source bundles.
+	 */
+	public AggregateResourceBundleLocator(List<String> bundleNames, ResourceBundleLocator delegate) {
+		super( delegate );
+
+		if ( bundleNames == null ) {
+			throw new IllegalArgumentException( "bundleNames must not be null." );
+		}
+
+		List<String> tmpBundleNames = new ArrayList<String>();
+		tmpBundleNames.addAll( bundleNames );
+
+		this.bundleNames = Collections.unmodifiableList( tmpBundleNames );
+	}
+
+	public ResourceBundle getResourceBundle(Locale locale) {
+		List<ResourceBundle> sourceBundles = new ArrayList<ResourceBundle>();
+
+		for ( String oneBundleName : bundleNames ) {
+			ResourceBundleLocator oneLocator =
+					new PlatformResourceBundleLocator( oneBundleName );
+
+			ResourceBundle oneBundle = oneLocator.getResourceBundle( locale );
+
+			if ( oneBundle != null ) {
+				sourceBundles.add( oneBundle );
+			}
+		}
+
+		ResourceBundle bundleFromDelegate = super.getResourceBundle( locale );
+
+		if ( bundleFromDelegate != null ) {
+			sourceBundles.add( bundleFromDelegate );
+		}
+
+		return sourceBundles.isEmpty() ? null : new AggregateBundle( sourceBundles );
+	}
+
+	/**
+	 * A {@link ResourceBundle} which's content is aggregated from multiple source bundles.
+	 * <p/>
+	 * This class is package-private for the sake of testability.
+	 *
+	 * @author Gunnar Morling
+	 */
+	static class AggregateBundle extends ResourceBundle {
+		private Map<String, Object> contents = new HashMap<String, Object>();
+
+		/**
+		 * Creates a new AggregateBundle.
+		 *
+		 * @param bundles A list of source bundles, which shall be merged into one
+		 * aggregated bundle. The newly created bundle will contain
+		 * all keys from all source bundles. In case a key occurs in
+		 * multiple source bundles, the value will be taken from the
+		 * first bundle containing the key.
+		 */
+		public AggregateBundle(List<ResourceBundle> bundles) {
+			if ( bundles != null ) {
+
+				for ( ResourceBundle bundle : bundles ) {
+					Enumeration<String> keys = bundle.getKeys();
+					while ( keys.hasMoreElements() ) {
+						String oneKey = keys.nextElement();
+						if ( !contents.containsKey( oneKey ) ) {
+							contents.put( oneKey, bundle.getObject( oneKey ) );
+						}
+					}
+				}
+			}
+		}
+
+		@Override
+		public Enumeration<String> getKeys() {
+			return new IteratorEnumeration<String>( contents.keySet().iterator() );
+		}
+
+		@Override
+		protected Object handleGetObject(String key) {
+			return contents.get( key );
+		}
+	}
+}

Added: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/CachingResourceBundleLocator.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/CachingResourceBundleLocator.java	                        (rev 0)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/CachingResourceBundleLocator.java	2010-03-21 22:08:06 UTC (rev 19034)
@@ -0,0 +1,56 @@
+// $Id: CachingResourceBundleLocator.java 17620 2009-10-04 19:19:28Z hardy.ferentschik $
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2010, Red Hat, Inc. and/or its affiliates, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* 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.hibernate.validator.engine.resourceloading;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * A {@link ResourceBundleLocator} implementation that wraps around another
+ * locator and caches values retrieved from that locator.
+ *
+ * @author Gunnar Morling
+ */
+public class CachingResourceBundleLocator extends DelegatingResourceBundleLocator {
+
+	private final ConcurrentMap<Locale, ResourceBundle> bundleCache = new ConcurrentHashMap<Locale, ResourceBundle>();
+
+	/**
+	 * Creates a new CachingResourceBundleLocator.
+	 *
+	 * @param delegate The locator from which the values actually will be retrieved.
+	 */
+	public CachingResourceBundleLocator(ResourceBundleLocator delegate) {
+		super( delegate );
+	}
+
+	public ResourceBundle getResourceBundle(Locale locale) {
+
+		if ( bundleCache.containsKey( locale ) ) {
+			return bundleCache.get( locale );
+		}
+
+		ResourceBundle bundle = super.getResourceBundle( locale );
+		if ( bundle != null ) {
+			bundleCache.put( locale, bundle );
+		}
+		return bundle;
+	}
+}

Added: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/DelegatingResourceBundleLocator.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/DelegatingResourceBundleLocator.java	                        (rev 0)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/DelegatingResourceBundleLocator.java	2010-03-21 22:08:06 UTC (rev 19034)
@@ -0,0 +1,41 @@
+// $Id: DelegatingResourceBundleLocator.java 17620 2009-10-04 19:19:28Z hardy.ferentschik $
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2009, Red Hat, Inc. and/or its affiliates, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* 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.hibernate.validator.engine.resourceloading;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+/**
+ * Abstract base for all {@link ResourceBundleLocator} implementations, that
+ * wish to delegate to some other locator.
+ *
+ * @author Gunnar Morling
+ */
+public abstract class DelegatingResourceBundleLocator implements ResourceBundleLocator {
+
+	private final ResourceBundleLocator delegate;
+
+	public DelegatingResourceBundleLocator(ResourceBundleLocator delegate) {
+		this.delegate = delegate;
+	}
+
+	public ResourceBundle getResourceBundle(Locale locale) {
+		return delegate == null ? null : delegate.getResourceBundle( locale );
+	}
+
+}

Added: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/PlatformResourceBundleLocator.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/PlatformResourceBundleLocator.java	                        (rev 0)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/PlatformResourceBundleLocator.java	2010-03-21 22:08:06 UTC (rev 19034)
@@ -0,0 +1,102 @@
+// $Id: PlatformResourceBundleLocator.java 17620 2009-10-04 19:19:28Z hardy.ferentschik $
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2009, Red Hat, Inc. and/or its affiliates, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* 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.hibernate.validator.engine.resourceloading;
+
+import java.security.AccessController;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.slf4j.Logger;
+
+import org.hibernate.validator.engine.ResourceBundleMessageInterpolator;
+import org.hibernate.validator.util.GetClassLoader;
+import org.hibernate.validator.util.LoggerFactory;
+
+/**
+ * A resource bundle locator, that loads resource bundles by simply
+ * invoking <code>ResourceBundle.loadBundle(...)</code>.
+ *
+ * @author Hardy Ferentschik
+ * @author Gunnar Morling
+ */
+public class PlatformResourceBundleLocator implements ResourceBundleLocator {
+
+	private static final Logger log = LoggerFactory.make();
+	private String bundleName;
+
+	public PlatformResourceBundleLocator(String bundleName) {
+		this.bundleName = bundleName;
+	}
+
+	/**
+	 * Search current thread classloader for the resource bundle. If not found,
+	 * search validator (this) classloader.
+	 *
+	 * @param locale The locale of the bundle to load.
+	 *
+	 * @return the resource bundle or <code>null</code> if none is found.
+	 */
+	public ResourceBundle getResourceBundle(Locale locale) {
+		ResourceBundle rb = null;
+		boolean isSecured = System.getSecurityManager() != null;
+		GetClassLoader action = GetClassLoader.fromContext();
+		ClassLoader classLoader = isSecured ? AccessController
+				.doPrivileged( action ) : action.run();
+
+		if ( classLoader != null ) {
+			rb = loadBundle(
+					classLoader, locale, bundleName
+							+ " not found by thread local classloader"
+			);
+		}
+		if ( rb == null ) {
+			action = GetClassLoader
+					.fromClass( ResourceBundleMessageInterpolator.class );
+			classLoader = isSecured ? AccessController.doPrivileged( action )
+					: action.run();
+			rb = loadBundle(
+					classLoader, locale, bundleName
+							+ " not found by validator classloader"
+			);
+		}
+		if ( log.isDebugEnabled() ) {
+			if ( rb != null ) {
+				log.debug( bundleName + " found" );
+			}
+			else {
+				log.debug( bundleName + " not found." );
+			}
+		}
+		return rb;
+	}
+
+	private ResourceBundle loadBundle(ClassLoader classLoader, Locale locale, String message) {
+		ResourceBundle rb = null;
+		try {
+			rb = ResourceBundle.getBundle(
+					bundleName, locale,
+					classLoader
+			);
+		}
+		catch ( MissingResourceException e ) {
+			log.trace( message );
+		}
+		return rb;
+	}
+}

Added: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/ResourceBundleLocator.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/ResourceBundleLocator.java	                        (rev 0)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/ResourceBundleLocator.java	2010-03-21 22:08:06 UTC (rev 19034)
@@ -0,0 +1,54 @@
+// $Id: ResourceBundleLocator.java 17620 2009-10-04 19:19:28Z hardy.ferentschik $
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2009, Red Hat, Inc. and/or its affiliates, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* 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.hibernate.validator.engine.resourceloading;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import org.hibernate.validator.engine.ResourceBundleMessageInterpolator;
+
+/**
+ * <p>
+ * Used by {@link ResourceBundleMessageInterpolator} to load resource bundles
+ * containing message texts to be displayed in case of validation errors.
+ * </p>
+ * <p>
+ * The default implementation provides access to the bundle "ValidationMessages"
+ * as described in the BV specification. By providing additional implementations
+ * of this interface, alternative ways of bundle loading can be realized, e.g.
+ * by loading bundles based on XML files or from a database.
+ * </p>
+ * <p/>
+ * TODO GM: Note, that all implementations must be thread-safe?
+ *
+ * @author Gunnar Morling
+ */
+public interface ResourceBundleLocator {
+
+	/**
+	 * Returns a resource bundle for the given locale.
+	 *
+	 * @param locale A locale, for which a resource bundle shall be retrieved. Must
+	 * not be null.
+	 *
+	 * @return A resource bundle for the given locale. May be null, if no such
+	 *         bundle exists.
+	 */
+	ResourceBundle getResourceBundle(Locale locale);
+
+}

Added: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/package.html
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/package.html	                        (rev 0)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/resourceloading/package.html	2010-03-21 22:08:06 UTC (rev 19034)
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+    <!--
+
+  JBoss, Home of Professional Open Source
+  Copyright 2010, Red Hat, Inc. and/or its affiliates, and individual contributors
+  by the @authors tag. See the copyright.txt in the distribution for a
+  full listing of individual contributors.
+
+  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.
+
+-->
+</head>
+<body>
+This package contains the ResourceBundleLocator interface and different implementations.
+</body>
+</html>

Added: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/util/IteratorEnumeration.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/util/IteratorEnumeration.java	                        (rev 0)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/util/IteratorEnumeration.java	2010-03-21 22:08:06 UTC (rev 19034)
@@ -0,0 +1,56 @@
+/*
+ * $Id:$
+ *
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat, Inc. and/or its affiliates, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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.hibernate.validator.util;
+
+import java.util.Enumeration;
+import java.util.Iterator;
+
+/**
+ * An {@link Enumeration} implementation, that wraps an {@link Iterator}. Can
+ * be used to integrate older APIs working with enumerations with iterators.
+ *
+ * @author Gunnar Morling
+ * @param <T> The enumerated type.
+ */
+public class IteratorEnumeration<T> implements Enumeration<T> {
+
+	private Iterator<T> source;
+
+	/**
+	 * Creates a new IterationEnumeration.
+	 *
+	 * @param source The source iterator. Must not be null.
+	 */
+	public IteratorEnumeration(Iterator<T> source) {
+
+		if ( source == null ) {
+			throw new IllegalArgumentException( "Source must not be null" );
+		}
+
+		this.source = source;
+	}
+
+	public boolean hasMoreElements() {
+		return source.hasNext();
+	}
+
+	public T nextElement() {
+		return source.next();
+	}
+}

Added: validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/HibernateValidatorConfigurationTest.java
===================================================================
--- validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/HibernateValidatorConfigurationTest.java	                        (rev 0)
+++ validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/HibernateValidatorConfigurationTest.java	2010-03-21 22:08:06 UTC (rev 19034)
@@ -0,0 +1,43 @@
+/*
+ * $Id:$
+ *
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat, Inc. and/or its affiliates, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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.hibernate.validator;
+
+import javax.validation.Validation;
+
+import org.testng.annotations.Test;
+
+import org.hibernate.validator.engine.resourceloading.ResourceBundleLocator;
+
+import static org.testng.Assert.assertNotNull;
+
+/**
+ * Test for {@link HibernateValidatorConfiguration}.
+ *
+ * @author Gunnar Morling
+ */
+public class HibernateValidatorConfigurationTest {
+
+	@Test
+	public void defaultResourceBundleLocatorCanBeRetrieved() {
+		HibernateValidatorConfiguration configure = Validation.byProvider( HibernateValidator.class ).configure();
+		ResourceBundleLocator defaultResourceBundleLocator = configure.getDefaultResourceBundleLocator();
+
+		assertNotNull( defaultResourceBundleLocator );
+	}
+}

Modified: validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/messageinterpolation/MessageInterpolationTest.java
===================================================================
--- validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/messageinterpolation/MessageInterpolationTest.java	2010-03-19 21:27:15 UTC (rev 19033)
+++ validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/messageinterpolation/MessageInterpolationTest.java	2010-03-21 22:08:06 UTC (rev 19034)
@@ -18,6 +18,7 @@
 package org.hibernate.validator.engine.messageinterpolation;
 
 import java.io.ByteArrayInputStream;
+import java.util.Locale;
 import java.util.PropertyResourceBundle;
 import java.util.ResourceBundle;
 import javax.validation.Configuration;
@@ -28,12 +29,14 @@
 import javax.validation.constraints.Min;
 import javax.validation.constraints.NotNull;
 
-import static org.testng.Assert.assertEquals;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import org.hibernate.validator.engine.ResourceBundleMessageInterpolator;
+import org.hibernate.validator.engine.resourceloading.ResourceBundleLocator;
 
+import static org.testng.Assert.assertEquals;
+
 /**
  * Tests for HV-184
  *
@@ -52,10 +55,19 @@
 		final ResourceBundle bundle = new PropertyResourceBundle(
 				new ByteArrayInputStream( lines.toString().getBytes() )
 		);
-		Configuration<?> config = ( Configuration<?> ) Validation.byDefaultProvider()
+		Configuration<?> config = Validation.byDefaultProvider()
 				.configure()
 				.messageInterpolator(
-						new ResourceBundleMessageInterpolator( bundle )
+						new ResourceBundleMessageInterpolator(
+								new ResourceBundleLocator() {
+
+									public ResourceBundle getResourceBundle(
+											Locale locale) {
+										return bundle;
+									}
+
+								}
+						)
 				);
 
 		ValidatorFactory factory = config.buildValidatorFactory();

Modified: validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/messageinterpolation/ResourceBundleMessageInterpolatorTest.java
===================================================================
--- validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/messageinterpolation/ResourceBundleMessageInterpolatorTest.java	2010-03-19 21:27:15 UTC (rev 19033)
+++ validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/messageinterpolation/ResourceBundleMessageInterpolatorTest.java	2010-03-21 22:08:06 UTC (rev 19034)
@@ -29,18 +29,20 @@
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Size;
 
-import static org.testng.Assert.assertEquals;
 import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;
 
 import org.hibernate.validator.engine.MessageInterpolatorContext;
 import org.hibernate.validator.engine.ResourceBundleMessageInterpolator;
+import org.hibernate.validator.engine.resourceloading.ResourceBundleLocator;
 import org.hibernate.validator.metadata.ConstraintDescriptorImpl;
 import org.hibernate.validator.metadata.ConstraintHelper;
 import org.hibernate.validator.metadata.ConstraintOrigin;
 import org.hibernate.validator.util.annotationfactory.AnnotationDescriptor;
 import org.hibernate.validator.util.annotationfactory.AnnotationFactory;
 
+import static org.testng.Assert.assertEquals;
+
 /**
  * Tests for message interpolation.
  *
@@ -75,7 +77,9 @@
 
 	@Test
 	public void testSuccessfulInterpolation() {
-		interpolator = new ResourceBundleMessageInterpolator( new TestResourceBundle() );
+		interpolator = new ResourceBundleMessageInterpolator(
+				new TestResourceBundleLocator()
+		);
 		MessageInterpolator.Context context = new MessageInterpolatorContext( notNullDescriptor, null );
 		String expected = "message interpolation successful";
 		String actual = interpolator.interpolate( "{simple.key}", context );
@@ -97,7 +101,9 @@
 	@Test
 	public void testMessageLiterals() {
 
-		interpolator = new ResourceBundleMessageInterpolator( new TestResourceBundle() );
+		interpolator = new ResourceBundleMessageInterpolator(
+				new TestResourceBundleLocator()
+		);
 		MessageInterpolator.Context context = new MessageInterpolatorContext( notNullDescriptor, null );
 
 		String expected = "{";
@@ -115,7 +121,9 @@
 
 	@Test
 	public void testUnSuccessfulInterpolation() {
-		interpolator = new ResourceBundleMessageInterpolator( new TestResourceBundle() );
+		interpolator = new ResourceBundleMessageInterpolator(
+				new TestResourceBundleLocator()
+		);
 		MessageInterpolator.Context context = new MessageInterpolatorContext( notNullDescriptor, null );
 
 		String expected = "foo";  // missing {}
@@ -129,7 +137,9 @@
 
 	@Test
 	public void testUnknownTokenInterpolation() {
-		interpolator = new ResourceBundleMessageInterpolator( new TestResourceBundle() );
+		interpolator = new ResourceBundleMessageInterpolator(
+				new TestResourceBundleLocator()
+		);
 		MessageInterpolator.Context context = new MessageInterpolatorContext( notNullDescriptor, null );
 
 		String expected = "{bar}";  // unknown token {}
@@ -139,7 +149,9 @@
 
 	@Test
 	public void testKeyWithDashes() {
-		interpolator = new ResourceBundleMessageInterpolator( new TestResourceBundle() );
+		interpolator = new ResourceBundleMessageInterpolator(
+				new TestResourceBundleLocator()
+		);
 		MessageInterpolator.Context context = new MessageInterpolatorContext( notNullDescriptor, null );
 
 		String expected = "message interpolation successful";  // unknown token {}
@@ -149,7 +161,9 @@
 
 	@Test
 	public void testKeyWithSpaces() {
-		interpolator = new ResourceBundleMessageInterpolator( new TestResourceBundle() );
+		interpolator = new ResourceBundleMessageInterpolator(
+				new TestResourceBundleLocator()
+		);
 		MessageInterpolator.Context context = new MessageInterpolatorContext( notNullDescriptor, null );
 
 		String expected = "message interpolation successful";  // unknown token {}
@@ -159,7 +173,9 @@
 
 	@Test
 	public void testDefaultInterpolation() {
-		interpolator = new ResourceBundleMessageInterpolator( new TestResourceBundle() );
+		interpolator = new ResourceBundleMessageInterpolator(
+				new TestResourceBundleLocator()
+		);
 		MessageInterpolator.Context context = new MessageInterpolatorContext( notNullDescriptor, null );
 
 		String expected = "may not be null";
@@ -207,7 +223,9 @@
 				max, new ConstraintHelper(), java.lang.annotation.ElementType.FIELD, ConstraintOrigin.DEFINED_LOCALLY
 		);
 
-		interpolator = new ResourceBundleMessageInterpolator( new TestResourceBundle() );
+		interpolator = new ResourceBundleMessageInterpolator(
+				new TestResourceBundleLocator()
+		);
 		MessageInterpolator.Context context = new MessageInterpolatorContext( constraintDescriptor, null );
 
 		String expected = "{replace.in.default.bundle2}";
@@ -233,7 +251,10 @@
 				max, new ConstraintHelper(), java.lang.annotation.ElementType.FIELD, ConstraintOrigin.DEFINED_LOCALLY
 		);
 
-		interpolator = new ResourceBundleMessageInterpolator( new TestResourceBundle() );
+		interpolator = new ResourceBundleMessageInterpolator(
+				new TestResourceBundleLocator()
+		);
+
 		MessageInterpolator.Context context = new MessageInterpolatorContext( constraintDescriptor, null );
 
 		String actual = interpolator.interpolate( max.message(), context );
@@ -243,10 +264,22 @@
 	}
 
 	/**
+	 * A dummy locator always returning a {@link TestResourceBundle}.
+	 */
+	private static class TestResourceBundleLocator implements ResourceBundleLocator {
+
+		private TestResourceBundle resourceBundle = new TestResourceBundle();
+
+		public ResourceBundle getResourceBundle(Locale locale) {
+			return resourceBundle;
+		}
+	}
+
+	/**
 	 * A dummy resource bundle which can be passed to the constructor of ResourceBundleMessageInterpolator to replace
 	 * the user specified resource bundle.
 	 */
-	class TestResourceBundle extends ResourceBundle implements Enumeration<String> {
+	private static class TestResourceBundle extends ResourceBundle implements Enumeration<String> {
 		private Map<String, String> testResources;
 		Iterator<String> iter;
 

Added: validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/resourceloading/AggregateBundleTest.java
===================================================================
--- validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/resourceloading/AggregateBundleTest.java	                        (rev 0)
+++ validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/resourceloading/AggregateBundleTest.java	2010-03-21 22:08:06 UTC (rev 19034)
@@ -0,0 +1,99 @@
+// $Id: AggregateBundleTest.java 17620 2009-10-04 19:19:28Z hardy.ferentschik $
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2010, Red Hat, Inc. and/or its affiliates, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* 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.hibernate.validator.engine.resourceloading;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.ResourceBundle;
+import java.util.Set;
+
+import org.testng.annotations.Test;
+
+import org.hibernate.validator.engine.resourceloading.AggregateResourceBundleLocator.AggregateBundle;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Test for {@link AggregateBundle}.
+ *
+ * @author Gunnar Morling
+ */
+public class AggregateBundleTest {
+
+	private final static String BUNDLE_NAME_1 = AggregateResourceBundleLocatorTest.class.getPackage()
+			.getName() + ".AggregateResourceBundleLocatorTestBundle1";
+
+	private final static String BUNDLE_NAME_2 = AggregateResourceBundleLocatorTest.class.getPackage()
+			.getName() + ".AggregateResourceBundleLocatorTestBundle2";
+
+	@Test
+	public void aggregateBundleContainsKeysOfAllSourceBundles() {
+		ResourceBundle bundle_1 = ResourceBundle.getBundle( BUNDLE_NAME_1 );
+		ResourceBundle bundle_2 = ResourceBundle.getBundle( BUNDLE_NAME_2 );
+
+		ResourceBundle aggregateBundle = new AggregateBundle( Arrays.asList( bundle_1, bundle_2 ) );
+
+		Set<String> actualKeys = getAsSet( aggregateBundle.getKeys() );
+		Set<String> expectedKeys = new HashSet<String>( Arrays.asList( "key_1", "key_2", "key_3" ) );
+
+		assertEquals( actualKeys, expectedKeys );
+	}
+
+	@Test
+	public void aggregateBundleWithNoSourceBundlesContainsNoKeys() {
+		ResourceBundle aggregateBundle = new AggregateBundle( Collections.<ResourceBundle>emptyList() );
+		assertTrue( getAsSet( aggregateBundle.getKeys() ).isEmpty() );
+	}
+
+	@Test
+	public void valuesProperlyRetrievedFromAggregateBundle() {
+		ResourceBundle bundle_1 = ResourceBundle.getBundle( BUNDLE_NAME_1 );
+		ResourceBundle bundle_2 = ResourceBundle.getBundle( BUNDLE_NAME_2 );
+
+		ResourceBundle aggregateBundle = new AggregateBundle( Arrays.asList( bundle_1, bundle_2 ) );
+
+		assertEquals(
+				aggregateBundle.getString( "key_1" ),
+				"value 1 from bundle 1",
+				"Value for key_1 should be retrieved from bundle 1"
+		);
+		assertEquals(
+				aggregateBundle.getString( "key_2" ),
+				"value 2 from bundle 1",
+				"Value for key_2 should be retrieved from bundle 1"
+		);
+		assertEquals(
+				aggregateBundle.getString( "key_3" ),
+				"value 3 from bundle 2",
+				"Value for key_3 should be retrieved from bundle 2"
+		);
+	}
+
+	private Set<String> getAsSet(Enumeration<String> e) {
+		Set<String> theValue = new HashSet<String>();
+
+		while ( e.hasMoreElements() ) {
+			theValue.add( e.nextElement() );
+		}
+
+		return theValue;
+	}
+}

Added: validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/resourceloading/AggregateResourceBundleLocatorTest.java
===================================================================
--- validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/resourceloading/AggregateResourceBundleLocatorTest.java	                        (rev 0)
+++ validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/resourceloading/AggregateResourceBundleLocatorTest.java	2010-03-21 22:08:06 UTC (rev 19034)
@@ -0,0 +1,96 @@
+// $Id: AggregateResourceBundleLocatorTest.java 17620 2009-10-04 19:19:28Z hardy.ferentschik $
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2010, Red Hat, Inc. and/or its affiliates, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* 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.hibernate.validator.engine.resourceloading;
+
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+/**
+ * Test for {@link AggregateResourceBundleLocator}.
+ *
+ * @author Gunnar Morling
+ */
+public class AggregateResourceBundleLocatorTest {
+
+	private final static String BUNDLE_NAME_1 =
+			AggregateResourceBundleLocatorTest.class.getPackage()
+					.getName() + ".AggregateResourceBundleLocatorTestBundle1";
+
+	private final static String BUNDLE_NAME_2 =
+			AggregateResourceBundleLocatorTest.class.getPackage()
+					.getName() + ".AggregateResourceBundleLocatorTestBundle2";
+
+	@Test
+	public void valuesAreRetrievedFromBothSourceBundles() {
+
+		ResourceBundleLocator locator =
+				new AggregateResourceBundleLocator( Arrays.asList( BUNDLE_NAME_1, BUNDLE_NAME_2 ) );
+
+		ResourceBundle resourceBundle = locator.getResourceBundle( Locale.getDefault() );
+
+		assertNotNull( resourceBundle );
+
+		//contained in bundle 1
+		assertEquals( resourceBundle.getString( "key_1" ), "value 1 from bundle 1" );
+
+		//contained in both bundles, bundle 1 comes first
+		assertEquals( resourceBundle.getString( "key_2" ), "value 2 from bundle 1" );
+
+		//contained in bundle 2
+		assertEquals( resourceBundle.getString( "key_3" ), "value 3 from bundle 2" );
+	}
+
+	@Test
+	public void valuesAreRetrievedFromDelegate() {
+
+		ResourceBundleLocator locator =
+				new AggregateResourceBundleLocator(
+						Arrays.asList( BUNDLE_NAME_1 ),
+						new PlatformResourceBundleLocator( BUNDLE_NAME_2 )
+				);
+
+		ResourceBundle resourceBundle = locator.getResourceBundle( Locale.ENGLISH );
+
+		assertNotNull( resourceBundle );
+
+		//contained in bundle 1
+		assertEquals( resourceBundle.getString( "key_1" ), "value 1 from bundle 1" );
+
+		//contained in both bundles, but bundle 1 is queried before bundle 2 (delegate)
+		assertEquals( resourceBundle.getString( "key_2" ), "value 2 from bundle 1" );
+
+		//contained in bundle 2
+		assertEquals( resourceBundle.getString( "key_3" ), "value 3 from bundle 2" );
+	}
+
+	@Test
+	public void nullReturnedAsBundleDoesNotExist() {
+
+		ResourceBundleLocator locator = new AggregateResourceBundleLocator( Arrays.asList( "foo" ) );
+		ResourceBundle resourceBundle = locator.getResourceBundle( Locale.ENGLISH );
+
+		assertNull( resourceBundle );
+	}
+}

Added: validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/resourceloading/AggregateResourceBundleLocatorTestBundle1.properties
===================================================================
--- validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/resourceloading/AggregateResourceBundleLocatorTestBundle1.properties	                        (rev 0)
+++ validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/resourceloading/AggregateResourceBundleLocatorTestBundle1.properties	2010-03-21 22:08:06 UTC (rev 19034)
@@ -0,0 +1,2 @@
+key_1=value 1 from bundle 1
+key_2=value 2 from bundle 1

Added: validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/resourceloading/AggregateResourceBundleLocatorTestBundle2.properties
===================================================================
--- validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/resourceloading/AggregateResourceBundleLocatorTestBundle2.properties	                        (rev 0)
+++ validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/engine/resourceloading/AggregateResourceBundleLocatorTestBundle2.properties	2010-03-21 22:08:06 UTC (rev 19034)
@@ -0,0 +1,2 @@
+key_2=value 2 from bundle 2
+key_3=value 3 from bundle 2
\ No newline at end of file



More information about the hibernate-commits mailing list