mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
93 Commits
v3.1.3.RELEASE
...
3.1.x
| Author | SHA1 | Date | |
|---|---|---|---|
| 7a7df66374 | |||
| 63bff1f068 | |||
| e7d2ac4427 | |||
| ae1b36dc4f | |||
| 75a84b50e3 | |||
| 72013f975c | |||
| 3a6e7b8020 | |||
| 8a93309c22 | |||
| 50ed863279 | |||
| f9bb4b3b80 | |||
| 01f31027d7 | |||
| a52396b342 | |||
| d547570bcd | |||
| a9290d80c4 | |||
| 1f52f09089 | |||
| e9616b7204 | |||
| bbd5fc6f35 | |||
| a420e84591 | |||
| b1267b95b9 | |||
| a3161632dd | |||
| e731746bb0 | |||
| f2d7d663b4 | |||
| acd268e48f | |||
| 9d77afc218 | |||
| f3d0beb955 | |||
| 245c61e278 | |||
| c374389a59 | |||
| b720804c6d | |||
| 1e888706d3 | |||
| 1bca2c01fd | |||
| 0c45f179b3 | |||
| eb492ed583 | |||
| cd8d72e3f8 | |||
| 8cf61c595e | |||
| 8422da4d3f | |||
| b93d266c75 | |||
| 57bf64364f | |||
| ebbcc4d9fb | |||
| 27f0142d9e | |||
| 8135442336 | |||
| aa388bc094 | |||
| e5783b1bfa | |||
| 7f7fbe8172 | |||
| 42ab04a299 | |||
| 5ae706ee10 | |||
| f449b5a5b3 | |||
| bdc8d798e2 | |||
| 30ad07a4e2 | |||
| 0ed1a21073 | |||
| bad2b900e9 | |||
| 9a04191c2b | |||
| a7889c9df4 | |||
| 1cd7600d8a | |||
| 418c2a8641 | |||
| 377f3eecb4 | |||
| d1efd4c3a1 | |||
| aba4be83a7 | |||
| 5bc58773fb | |||
| 39fe8eebb1 | |||
| 778693f9f8 | |||
| aa3e0be1a0 | |||
| 28c7760c79 | |||
| cc11b811ea | |||
| 8abe1f055e | |||
| 12c4f8e7f9 | |||
| b9ff29ec7e | |||
| a551cbcf9d | |||
| c207882fdc | |||
| ab21bd4266 | |||
| 2ec41af799 | |||
| eb24365556 | |||
| 3a31efaf31 | |||
| 751c429897 | |||
| f19bc572da | |||
| 81348516ff | |||
| a00e00bd47 | |||
| b0fce78abf | |||
| 90fbeb1643 | |||
| 7384c3d82f | |||
| b016c8db91 | |||
| ceb8ce0a76 | |||
| d34c392c53 | |||
| 2b4e25b6f3 | |||
| 9929810610 | |||
| 4058f693e5 | |||
| 959ab7a5db | |||
| bed2134736 | |||
| dd68fecbf2 | |||
| 667438ddb0 | |||
| f4532ad704 | |||
| 0deacb9fa1 | |||
| 0ee12563b0 | |||
| e0b29b708c |
@@ -3,6 +3,43 @@ SPRING FRAMEWORK CHANGELOG
|
||||
http://www.springsource.org
|
||||
|
||||
|
||||
Changes in version 3.1.4 (2013-01-23)
|
||||
-------------------------------------
|
||||
|
||||
* overhauled non-void JavaBean write method support (SPR-10029)
|
||||
* SpEL support for static finals on interfaces (SPR-10125)
|
||||
* BridgeMethodResolver properly handles bridge methods in interfaces (SPR-9330)
|
||||
* LocalVariableTableParameterNameDiscoverer works for bridge methods as well (SPR-9429)
|
||||
* CachedIntrospectionResults uses full WeakReference for any non-safe ClassLoader arrangement (SPR-10028)
|
||||
* CachedIntrospectionResults.clearClassLoader(null) removes cached classes for the system class loader (SPR-9189)
|
||||
* DefaultListableBeanFactory avoids wide/interleaved metadata locks to avoid deadlock potential (SPR-10020)
|
||||
* fixed CGLIB proxy class leaks through further equals/hashCode implementations in Spring AOP pointcuts (SPR-8008)
|
||||
* ConfigurationClassPostProcessor consistently uses ClassLoader, not loading core JDK classes via ASM (SPR-10058)
|
||||
* MessageSourceResourceBundle overrides JDK 1.6 containsKey method, avoiding NPE in getKeys (SPR-10136)
|
||||
* SpringBeanAutowiringInterceptor eagerly releases BeanFactory if post-construction fails (SPR-10013)
|
||||
* added "exposeAccessContext" flag JndiRmiClientInterceptor/ProxyFactoryBean (for WebLogic; SPR-9428)
|
||||
* MBeanExporter does not log warnings for manually unregistered MBeans (SPR-9451)
|
||||
* AbstractCacheManager accepts no caches defined, allowing for EHCache default cache setup (SPR-7955)
|
||||
* reintroduced "mode" and "proxy-target-class" attributes in spring-task-3.1.xsd (SPR-10177)
|
||||
* improved defensiveness of JDBC 3.0 ParameterMetaData calls for parameter type determination (SPR-10084)
|
||||
* JpaTransactionManager etc finds default EntityManagerFactory in parent context as well (SPR-10160)
|
||||
* tx timeouts for JPA translate to "javax.persistence.query.timeout" only (for EclipseLink compatibility; SPR-10068)
|
||||
* DefaultMessageListenerContainer allows for concurrent subscription consumers on WebLogic/ActiveMQ (SPR-10037)
|
||||
* DefaultMessageListenerContainer clears resources of paused tasks when shutting down after stop (SPR-10092)
|
||||
* JmsTemplate uses configured receiveTimeout if shorter than remaining transaction timeout (SPR-10109)
|
||||
* added MappingJackson2MessageConverter for JMS (SPR-10099)
|
||||
* ResourceDatabasePopulator explicitly closes its LineNumberReader (SPR-9912)
|
||||
* MimeMessageHelper encodes attachment filename if not ASCII compliant (SPR-9258)
|
||||
* FreeMarkerConfigurationFactory properly supports TemplateLoaders when recreating Configurations (SPR-9389)
|
||||
* SpringContextResourceAdapter implements equals/hashCode according to the JCA 1.5 contract (SPR-9162)
|
||||
* ContextLoader properly detects pre-refreshed WebApplicationContext (SPR-9996)
|
||||
* AbstractCachingViewResolver uses a cache limit of 1024 by default, avoiding overflow for redirect URLs (SPR-10065)
|
||||
* fixed HierarchicalUriComponents equals implementation (SPR-10088)
|
||||
* MockHttpServletRequest's getParameter(Values) returns null for null parameter name (SPR-10192)
|
||||
* MockHttpServletResponse's getHeaderNames declares Collection instead of Set for Servlet 3.0 compatibility (SPR-9885)
|
||||
* ResourcePropertySource supports JDK 5-style Properties XML (SPR-9896)
|
||||
|
||||
|
||||
Changes in version 3.1.3 (2012-10-31)
|
||||
-------------------------------------
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
SPRING FRAMEWORK 3.1.3 (October 2012)
|
||||
---------------------------------------
|
||||
SPRING FRAMEWORK 3.1.4 (January 2013)
|
||||
-------------------------------------
|
||||
http://www.springsource.org
|
||||
|
||||
1. INTRODUCTION
|
||||
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
version=3.1.3
|
||||
version=3.1.5
|
||||
|
||||
natural.name=spring-framework
|
||||
project.name=Spring Framework
|
||||
@@ -17,16 +17,16 @@ spring.osgi.range="${spring.osgi.range.nq}"
|
||||
aj.osgi.range="[1.6.8, 2.0.0)"
|
||||
|
||||
## For GA releases
|
||||
release.type=release
|
||||
build.stamp=RELEASE
|
||||
#release.type=release
|
||||
#build.stamp=RELEASE
|
||||
|
||||
## For milestone releases
|
||||
#release.type=milestone
|
||||
#build.stamp=M1
|
||||
|
||||
## For trunk development / ci builds
|
||||
#release.type=integration
|
||||
#build.stamp=BUILD-SNAPSHOT
|
||||
release.type=integration
|
||||
build.stamp=BUILD-SNAPSHOT
|
||||
|
||||
#bundlor
|
||||
fail.on.warnings=false
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aop</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+18
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -681,6 +681,23 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedence
|
||||
public boolean matches(Method method, Class targetClass) {
|
||||
return !this.adviceMethod.equals(method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof AdviceExcludingMethodMatcher)) {
|
||||
return false;
|
||||
}
|
||||
AdviceExcludingMethodMatcher otherMm = (AdviceExcludingMethodMatcher) other;
|
||||
return this.adviceMethod.equals(otherMm.adviceMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.adviceMethod.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -271,7 +271,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyConfig
|
||||
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
|
||||
Object cacheKey = getCacheKey(beanClass, beanName);
|
||||
|
||||
if (!this.targetSourcedBeans.contains(cacheKey)) {
|
||||
if (!this.targetSourcedBeans.contains(beanName)) {
|
||||
if (this.advisedBeans.contains(cacheKey) || this.nonAdvisedBeans.contains(cacheKey)) {
|
||||
return null;
|
||||
}
|
||||
@@ -371,17 +371,18 @@ public abstract class AbstractAutoProxyCreator extends ProxyConfig
|
||||
/**
|
||||
* Return whether the given bean class represents an infrastructure class
|
||||
* that should never be proxied.
|
||||
* <p>Default implementation considers Advisors, Advices and
|
||||
* AbstractAutoProxyCreators as infrastructure classes.
|
||||
* <p>The default implementation considers Advices, Advisors and
|
||||
* AopInfrastructureBeans as infrastructure classes.
|
||||
* @param beanClass the class of the bean
|
||||
* @return whether the bean represents an infrastructure class
|
||||
* @see org.aopalliance.aop.Advice
|
||||
* @see org.springframework.aop.Advisor
|
||||
* @see org.aopalliance.intercept.MethodInterceptor
|
||||
* @see org.springframework.aop.framework.AopInfrastructureBean
|
||||
* @see #shouldSkip
|
||||
*/
|
||||
protected boolean isInfrastructureClass(Class<?> beanClass) {
|
||||
boolean retVal = Advisor.class.isAssignableFrom(beanClass) ||
|
||||
Advice.class.isAssignableFrom(beanClass) ||
|
||||
boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
|
||||
Advisor.class.isAssignableFrom(beanClass) ||
|
||||
AopInfrastructureBean.class.isAssignableFrom(beanClass);
|
||||
if (retVal && logger.isTraceEnabled()) {
|
||||
logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
|
||||
|
||||
+18
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -56,4 +56,21 @@ public class AnnotationMethodMatcher extends StaticMethodMatcher {
|
||||
return (specificMethod != method && specificMethod.isAnnotationPresent(this.annotationType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof AnnotationMethodMatcher)) {
|
||||
return false;
|
||||
}
|
||||
AnnotationMethodMatcher otherMm = (AnnotationMethodMatcher) other;
|
||||
return this.annotationType.equals(otherMm.annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.annotationType.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-asm</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aspects</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+20
-21
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,6 +22,7 @@ import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@@ -32,7 +33,6 @@ import java.util.WeakHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -100,9 +100,6 @@ public class CachedIntrospectionResults {
|
||||
* @param classLoader the ClassLoader to clear the cache for
|
||||
*/
|
||||
public static void clearClassLoader(ClassLoader classLoader) {
|
||||
if (classLoader == null) {
|
||||
return;
|
||||
}
|
||||
synchronized (classCache) {
|
||||
for (Iterator<Class> it = classCache.keySet().iterator(); it.hasNext();) {
|
||||
Class beanClass = it.next();
|
||||
@@ -140,20 +137,16 @@ public class CachedIntrospectionResults {
|
||||
results = (CachedIntrospectionResults) value;
|
||||
}
|
||||
if (results == null) {
|
||||
// On JDK 1.5 and higher, it is almost always safe to cache the bean class...
|
||||
// The sole exception is a custom BeanInfo class being provided in a non-safe ClassLoader.
|
||||
boolean fullyCacheable =
|
||||
ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) ||
|
||||
isClassLoaderAccepted(beanClass.getClassLoader());
|
||||
if (fullyCacheable || !ClassUtils.isPresent(beanClass.getName() + "BeanInfo", beanClass.getClassLoader())) {
|
||||
results = new CachedIntrospectionResults(beanClass, fullyCacheable);
|
||||
if (ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) ||
|
||||
isClassLoaderAccepted(beanClass.getClassLoader())) {
|
||||
results = new CachedIntrospectionResults(beanClass);
|
||||
classCache.put(beanClass, results);
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Not strongly caching class [" + beanClass.getName() + "] because it is not cache-safe");
|
||||
}
|
||||
results = new CachedIntrospectionResults(beanClass, true);
|
||||
results = new CachedIntrospectionResults(beanClass);
|
||||
classCache.put(beanClass, new WeakReference<CachedIntrospectionResults>(results));
|
||||
}
|
||||
}
|
||||
@@ -187,12 +180,12 @@ public class CachedIntrospectionResults {
|
||||
* @param parent the parent ClassLoader to check for
|
||||
*/
|
||||
private static boolean isUnderneathClassLoader(ClassLoader candidate, ClassLoader parent) {
|
||||
if (candidate == null) {
|
||||
return false;
|
||||
}
|
||||
if (candidate == parent) {
|
||||
return true;
|
||||
}
|
||||
if (candidate == null) {
|
||||
return false;
|
||||
}
|
||||
ClassLoader classLoaderToCheck = candidate;
|
||||
while (classLoaderToCheck != null) {
|
||||
classLoaderToCheck = classLoaderToCheck.getParent();
|
||||
@@ -216,12 +209,20 @@ public class CachedIntrospectionResults {
|
||||
* @param beanClass the bean class to analyze
|
||||
* @throws BeansException in case of introspection failure
|
||||
*/
|
||||
private CachedIntrospectionResults(Class beanClass, boolean cacheFullMetadata) throws BeansException {
|
||||
private CachedIntrospectionResults(Class beanClass) throws BeansException {
|
||||
try {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Getting BeanInfo for class [" + beanClass.getName() + "]");
|
||||
}
|
||||
this.beanInfo = new ExtendedBeanInfo(Introspector.getBeanInfo(beanClass));
|
||||
BeanInfo originalBeanInfo = Introspector.getBeanInfo(beanClass);
|
||||
BeanInfo extendedBeanInfo = null;
|
||||
for (Method method : beanClass.getMethods()) {
|
||||
if (ExtendedBeanInfo.isCandidateWriteMethod(method)) {
|
||||
extendedBeanInfo = new ExtendedBeanInfo(originalBeanInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.beanInfo = extendedBeanInfo != null ? extendedBeanInfo : originalBeanInfo;
|
||||
|
||||
// Immediately remove class from Introspector cache, to allow for proper
|
||||
// garbage collection on class loader shutdown - we cache it here anyway,
|
||||
@@ -252,9 +253,7 @@ public class CachedIntrospectionResults {
|
||||
(pd.getPropertyEditorClass() != null ?
|
||||
"; editor [" + pd.getPropertyEditorClass().getName() + "]" : ""));
|
||||
}
|
||||
if (cacheFullMetadata) {
|
||||
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
|
||||
}
|
||||
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
|
||||
this.propertyDescriptorCache.put(pd.getName(), pd);
|
||||
}
|
||||
}
|
||||
|
||||
+573
-296
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,9 +16,8 @@
|
||||
|
||||
package org.springframework.beans;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.awt.Image;
|
||||
|
||||
import java.beans.BeanDescriptor;
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.EventSetDescriptor;
|
||||
@@ -27,310 +26,202 @@ import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.MethodDescriptor;
|
||||
import java.beans.PropertyDescriptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.SortedSet;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import static org.springframework.beans.PropertyDescriptorUtils.*;
|
||||
|
||||
/**
|
||||
* Decorates a standard {@link BeanInfo} object (likely created created by
|
||||
* {@link Introspector#getBeanInfo(Class)}) by including non-void returning setter
|
||||
* methods in the collection of {@link #getPropertyDescriptors() property descriptors}.
|
||||
* Both regular and
|
||||
* Decorator for a standard {@link BeanInfo} object, e.g. as created by
|
||||
* {@link Introspector#getBeanInfo(Class)}, designed to discover and register static
|
||||
* and/or non-void returning setter methods. For example:
|
||||
* <pre>{@code
|
||||
* public class Bean {
|
||||
* private Foo foo;
|
||||
*
|
||||
* public Foo getFoo() {
|
||||
* return this.foo;
|
||||
* }
|
||||
*
|
||||
* public Bean setFoo(Foo foo) {
|
||||
* this.foo = foo;
|
||||
* return this;
|
||||
* }
|
||||
* }</pre>
|
||||
* The standard JavaBeans {@code Introspector} will discover the {@code getFoo} read
|
||||
* method, but will bypass the {@code #setFoo(Foo)} write method, because its non-void
|
||||
* returning signature does not comply with the JavaBeans specification.
|
||||
* {@code ExtendedBeanInfo}, on the other hand, will recognize and include it. This is
|
||||
* designed to allow APIs with "builder" or method-chaining style setter signatures to be
|
||||
* used within Spring {@code <beans>} XML. {@link #getPropertyDescriptors()} returns all
|
||||
* existing property descriptors from the wrapped {@code BeanInfo} as well any added for
|
||||
* non-void returning setters. Both standard ("non-indexed") and
|
||||
* <a href="http://docs.oracle.com/javase/tutorial/javabeans/writing/properties.html">
|
||||
* indexed properties</a> are fully supported.
|
||||
*
|
||||
* <p>The wrapped {@code BeanInfo} object is not modified in any way.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see #ExtendedBeanInfo(BeanInfo)
|
||||
* @see CachedIntrospectionResults
|
||||
*/
|
||||
class ExtendedBeanInfo implements BeanInfo {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final BeanInfo delegate;
|
||||
|
||||
private final SortedSet<PropertyDescriptor> propertyDescriptors =
|
||||
new TreeSet<PropertyDescriptor>(new PropertyDescriptorComparator());
|
||||
private final Set<PropertyDescriptor> propertyDescriptors =
|
||||
new TreeSet<PropertyDescriptor>(new PropertyDescriptorComparator());
|
||||
|
||||
|
||||
/**
|
||||
* Wrap the given delegate {@link BeanInfo} instance and find any non-void returning
|
||||
* setter methods, creating and adding a {@link PropertyDescriptor} for each.
|
||||
*
|
||||
* <p>Note that the wrapped {@code BeanInfo} is modified by this process.
|
||||
*
|
||||
* Wrap the given {@link BeanInfo} instance; copy all its existing property descriptors
|
||||
* locally, wrapping each in a custom {@link SimpleIndexedPropertyDescriptor indexed} or
|
||||
* {@link SimpleNonIndexedPropertyDescriptor non-indexed} {@code PropertyDescriptor}
|
||||
* variant that bypasses default JDK weak/soft reference management; then search
|
||||
* through its method descriptors to find any non-void returning write methods and
|
||||
* update or create the corresponding {@link PropertyDescriptor} for each one found.
|
||||
* @param delegate the wrapped {@code BeanInfo}, which is never modified
|
||||
* @throws IntrospectionException if any problems occur creating and adding new
|
||||
* property descriptors
|
||||
* @see #getPropertyDescriptors()
|
||||
* @throws IntrospectionException if any problems occur creating and adding new {@code PropertyDescriptors}
|
||||
*/
|
||||
public ExtendedBeanInfo(BeanInfo delegate) throws IntrospectionException {
|
||||
this.delegate = delegate;
|
||||
|
||||
ALL_METHODS:
|
||||
for (MethodDescriptor md : delegate.getMethodDescriptors()) {
|
||||
Method method = resolveMethod(md.getMethod());
|
||||
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
|
||||
this.propertyDescriptors.add(pd instanceof IndexedPropertyDescriptor ?
|
||||
new SimpleIndexedPropertyDescriptor((IndexedPropertyDescriptor) pd) :
|
||||
new SimpleNonIndexedPropertyDescriptor(pd));
|
||||
}
|
||||
|
||||
// bypass non-getter java.lang.Class methods for efficiency
|
||||
if (ReflectionUtils.isObjectMethod(method) && !method.getName().startsWith("get")) {
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
|
||||
// is the method a NON-INDEXED setter? ignore return type in order to capture non-void signatures
|
||||
if (method.getName().startsWith("set") && method.getParameterTypes().length == 1) {
|
||||
String propertyName = propertyNameFor(method);
|
||||
if(propertyName.length() == 0) {
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
|
||||
Method readMethod = readMethodFor(pd);
|
||||
Method writeMethod = writeMethodFor(pd);
|
||||
// has the setter already been found by the wrapped BeanInfo?
|
||||
if (writeMethod != null
|
||||
&& writeMethod.getName().equals(method.getName())) {
|
||||
// yes -> copy it, including corresponding getter method (if any -- may be null)
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethod);
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
// has a getter corresponding to this setter already been found by the wrapped BeanInfo?
|
||||
if (readMethod != null
|
||||
&& readMethod.getName().equals(getterMethodNameFor(propertyName))
|
||||
&& readMethod.getReturnType().equals(method.getParameterTypes()[0])) {
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, method);
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
}
|
||||
// the setter method was not found by the wrapped BeanInfo -> add a new PropertyDescriptor for it
|
||||
// no corresponding getter was detected, so the 'read method' parameter is null.
|
||||
this.addOrUpdatePropertyDescriptor(null, propertyName, null, method);
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
|
||||
// is the method an INDEXED setter? ignore return type in order to capture non-void signatures
|
||||
if (method.getName().startsWith("set") && method.getParameterTypes().length == 2 && method.getParameterTypes()[0].equals(int.class)) {
|
||||
String propertyName = propertyNameFor(method);
|
||||
if(propertyName.length() == 0) {
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
DELEGATE_PD:
|
||||
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
|
||||
if (!(pd instanceof IndexedPropertyDescriptor)) {
|
||||
continue DELEGATE_PD;
|
||||
}
|
||||
IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
|
||||
Method readMethod = readMethodFor(ipd);
|
||||
Method writeMethod = writeMethodFor(ipd);
|
||||
Method indexedReadMethod = indexedReadMethodFor(ipd);
|
||||
Method indexedWriteMethod = indexedWriteMethodFor(ipd);
|
||||
// has the setter already been found by the wrapped BeanInfo?
|
||||
if (!(indexedWriteMethod != null
|
||||
&& indexedWriteMethod.getName().equals(method.getName()))) {
|
||||
indexedWriteMethod = method;
|
||||
}
|
||||
// yes -> copy it, including corresponding getter method (if any -- may be null)
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethod, indexedReadMethod, indexedWriteMethod);
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
// the INDEXED setter method was not found by the wrapped BeanInfo -> add a new PropertyDescriptor
|
||||
// for it. no corresponding INDEXED getter was detected, so the 'indexed read method' parameter is null.
|
||||
this.addOrUpdatePropertyDescriptor(null, propertyName, null, null, null, method);
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
|
||||
// the method is not a setter, but is it a getter?
|
||||
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
|
||||
// have we already copied this read method to a property descriptor locally?
|
||||
String propertyName = pd.getName();
|
||||
Method readMethod = readMethodFor(pd);
|
||||
Method mostSpecificReadMethod = ClassUtils.getMostSpecificMethod(readMethod, method.getDeclaringClass());
|
||||
for (PropertyDescriptor existingPD : this.propertyDescriptors) {
|
||||
if (method.equals(mostSpecificReadMethod)
|
||||
&& existingPD.getName().equals(propertyName)) {
|
||||
if (readMethodFor(existingPD) == null) {
|
||||
// no -> add it now
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, method, writeMethodFor(pd));
|
||||
}
|
||||
// yes -> do not add a duplicate
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
}
|
||||
if (method.equals(mostSpecificReadMethod)
|
||||
|| (pd instanceof IndexedPropertyDescriptor && method.equals(indexedReadMethodFor((IndexedPropertyDescriptor) pd)))) {
|
||||
// yes -> copy it, including corresponding setter method (if any -- may be null)
|
||||
if (pd instanceof IndexedPropertyDescriptor) {
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethodFor(pd), indexedReadMethodFor((IndexedPropertyDescriptor)pd), indexedWriteMethodFor((IndexedPropertyDescriptor)pd));
|
||||
} else {
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethodFor(pd));
|
||||
}
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
}
|
||||
for (Method method : findCandidateWriteMethods(delegate.getMethodDescriptors())) {
|
||||
handleCandidateWriteMethod(method);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static Method resolveMethod(Method method) {
|
||||
return BridgeMethodResolver.findBridgedMethod(method);
|
||||
}
|
||||
|
||||
private static Method readMethodFor(PropertyDescriptor pd) {
|
||||
return resolveMethod(pd.getReadMethod());
|
||||
}
|
||||
|
||||
private static Method writeMethodFor(PropertyDescriptor pd) {
|
||||
return resolveMethod(pd.getWriteMethod());
|
||||
}
|
||||
|
||||
private static Method indexedReadMethodFor(IndexedPropertyDescriptor ipd) {
|
||||
return resolveMethod(ipd.getIndexedReadMethod());
|
||||
}
|
||||
|
||||
private static Method indexedWriteMethodFor(IndexedPropertyDescriptor ipd) {
|
||||
return resolveMethod(ipd.getIndexedWriteMethod());
|
||||
}
|
||||
|
||||
private void addOrUpdatePropertyDescriptor(PropertyDescriptor pd, String propertyName, Method readMethod, Method writeMethod) throws IntrospectionException {
|
||||
addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethod, null, null);
|
||||
}
|
||||
|
||||
private void addOrUpdatePropertyDescriptor(PropertyDescriptor pd, String propertyName, Method readMethod, Method writeMethod, Method indexedReadMethod, Method indexedWriteMethod) throws IntrospectionException {
|
||||
Assert.notNull(propertyName, "propertyName may not be null");
|
||||
propertyName = pd == null ? propertyName : pd.getName();
|
||||
for (PropertyDescriptor existingPD : this.propertyDescriptors) {
|
||||
if (existingPD.getName().equals(propertyName)) {
|
||||
// is there already a descriptor that captures this read method or its corresponding write method?
|
||||
if (readMethodFor(existingPD) != null) {
|
||||
if (readMethod != null && readMethodFor(existingPD).getReturnType() != readMethod.getReturnType()
|
||||
|| writeMethod != null && readMethodFor(existingPD).getReturnType() != writeMethod.getParameterTypes()[0]) {
|
||||
// no -> add a new descriptor for it below
|
||||
break;
|
||||
}
|
||||
}
|
||||
// update the existing descriptor's read method
|
||||
if (readMethod != null) {
|
||||
try {
|
||||
existingPD.setReadMethod(readMethod);
|
||||
} catch (IntrospectionException ex) {
|
||||
// there is a conflicting setter method present -> null it out and try again
|
||||
existingPD.setWriteMethod(null);
|
||||
existingPD.setReadMethod(readMethod);
|
||||
}
|
||||
}
|
||||
|
||||
// is there already a descriptor that captures this write method or its corresponding read method?
|
||||
if (writeMethodFor(existingPD) != null) {
|
||||
if (readMethod != null && writeMethodFor(existingPD).getParameterTypes()[0] != readMethod.getReturnType()
|
||||
|| writeMethod != null && writeMethodFor(existingPD).getParameterTypes()[0] != writeMethod.getParameterTypes()[0]) {
|
||||
// no -> add a new descriptor for it below
|
||||
break;
|
||||
}
|
||||
}
|
||||
// update the existing descriptor's write method
|
||||
if (writeMethod != null
|
||||
&& !(existingPD instanceof IndexedPropertyDescriptor &&
|
||||
!writeMethod.getParameterTypes()[0].isArray())) {
|
||||
existingPD.setWriteMethod(writeMethod);
|
||||
}
|
||||
|
||||
// is this descriptor indexed?
|
||||
if (existingPD instanceof IndexedPropertyDescriptor) {
|
||||
IndexedPropertyDescriptor existingIPD = (IndexedPropertyDescriptor) existingPD;
|
||||
|
||||
// is there already a descriptor that captures this indexed read method or its corresponding indexed write method?
|
||||
if (indexedReadMethodFor(existingIPD) != null) {
|
||||
if (indexedReadMethod != null && indexedReadMethodFor(existingIPD).getReturnType() != indexedReadMethod.getReturnType()
|
||||
|| indexedWriteMethod != null && indexedReadMethodFor(existingIPD).getReturnType() != indexedWriteMethod.getParameterTypes()[1]) {
|
||||
// no -> add a new descriptor for it below
|
||||
break;
|
||||
}
|
||||
}
|
||||
// update the existing descriptor's indexed read method
|
||||
try {
|
||||
if (indexedReadMethod != null) {
|
||||
existingIPD.setIndexedReadMethod(indexedReadMethod);
|
||||
}
|
||||
} catch (IntrospectionException ex) {
|
||||
// there is a conflicting indexed setter method present -> null it out and try again
|
||||
existingIPD.setIndexedWriteMethod(null);
|
||||
existingIPD.setIndexedReadMethod(indexedReadMethod);
|
||||
}
|
||||
|
||||
// is there already a descriptor that captures this indexed write method or its corresponding indexed read method?
|
||||
if (indexedWriteMethodFor(existingIPD) != null) {
|
||||
if (indexedReadMethod != null && indexedWriteMethodFor(existingIPD).getParameterTypes()[1] != indexedReadMethod.getReturnType()
|
||||
|| indexedWriteMethod != null && indexedWriteMethodFor(existingIPD).getParameterTypes()[1] != indexedWriteMethod.getParameterTypes()[1]) {
|
||||
// no -> add a new descriptor for it below
|
||||
break;
|
||||
}
|
||||
}
|
||||
// update the existing descriptor's indexed write method
|
||||
if (indexedWriteMethod != null) {
|
||||
existingIPD.setIndexedWriteMethod(indexedWriteMethod);
|
||||
}
|
||||
}
|
||||
|
||||
// the descriptor has been updated -> return immediately
|
||||
return;
|
||||
private List<Method> findCandidateWriteMethods(MethodDescriptor[] methodDescriptors) {
|
||||
List<Method> matches = new ArrayList<Method>();
|
||||
for (MethodDescriptor methodDescriptor : methodDescriptors) {
|
||||
Method method = methodDescriptor.getMethod();
|
||||
if (isCandidateWriteMethod(method)) {
|
||||
matches.add(method);
|
||||
}
|
||||
}
|
||||
// sort non-void returning write methods to guard against the ill effects of
|
||||
// non-deterministic sorting of methods returned from Class#getDeclaredMethods
|
||||
// under JDK 7. See http://bugs.sun.com/view_bug.do?bug_id=7023180
|
||||
Collections.sort(matches, new Comparator<Method>() {
|
||||
public int compare(Method m1, Method m2) {
|
||||
return m2.toString().compareTo(m1.toString());
|
||||
}
|
||||
});
|
||||
return matches;
|
||||
}
|
||||
|
||||
// we haven't yet seen read or write methods for this property -> add a new descriptor
|
||||
if (pd == null) {
|
||||
try {
|
||||
if (indexedReadMethod == null && indexedWriteMethod == null) {
|
||||
pd = new PropertyDescriptor(propertyName, readMethod, writeMethod);
|
||||
}
|
||||
else {
|
||||
pd = new IndexedPropertyDescriptor(propertyName, readMethod, writeMethod, indexedReadMethod, indexedWriteMethod);
|
||||
}
|
||||
this.propertyDescriptors.add(pd);
|
||||
} catch (IntrospectionException ex) {
|
||||
logger.debug(format("Could not create new PropertyDescriptor for readMethod [%s] writeMethod [%s] " +
|
||||
"indexedReadMethod [%s] indexedWriteMethod [%s] for property [%s]. Reason: %s",
|
||||
readMethod, writeMethod, indexedReadMethod, indexedWriteMethod, propertyName, ex.getMessage()));
|
||||
// suppress exception and attempt to continue
|
||||
public static boolean isCandidateWriteMethod(Method method) {
|
||||
String methodName = method.getName();
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
int nParams = parameterTypes.length;
|
||||
if (methodName.length() > 3 && methodName.startsWith("set") &&
|
||||
Modifier.isPublic(method.getModifiers()) &&
|
||||
(
|
||||
!void.class.isAssignableFrom(method.getReturnType()) ||
|
||||
Modifier.isStatic(method.getModifiers())
|
||||
) &&
|
||||
(nParams == 1 || (nParams == 2 && parameterTypes[0].equals(int.class)))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void handleCandidateWriteMethod(Method method) throws IntrospectionException {
|
||||
int nParams = method.getParameterTypes().length;
|
||||
String propertyName = propertyNameFor(method);
|
||||
Class<?> propertyType = method.getParameterTypes()[nParams-1];
|
||||
PropertyDescriptor existingPD = findExistingPropertyDescriptor(propertyName, propertyType);
|
||||
if (nParams == 1) {
|
||||
if (existingPD == null) {
|
||||
this.propertyDescriptors.add(
|
||||
new SimpleNonIndexedPropertyDescriptor(propertyName, null, method));
|
||||
}
|
||||
else {
|
||||
existingPD.setWriteMethod(method);
|
||||
}
|
||||
}
|
||||
else if (nParams == 2) {
|
||||
if (existingPD == null) {
|
||||
this.propertyDescriptors.add(
|
||||
new SimpleIndexedPropertyDescriptor(
|
||||
propertyName, null, null, null, method));
|
||||
}
|
||||
else if (existingPD instanceof IndexedPropertyDescriptor) {
|
||||
((IndexedPropertyDescriptor)existingPD).setIndexedWriteMethod(method);
|
||||
}
|
||||
else {
|
||||
this.propertyDescriptors.remove(existingPD);
|
||||
this.propertyDescriptors.add(
|
||||
new SimpleIndexedPropertyDescriptor(
|
||||
propertyName, existingPD.getReadMethod(),
|
||||
existingPD.getWriteMethod(), null, method));
|
||||
}
|
||||
}
|
||||
else {
|
||||
pd.setWriteMethod(null);
|
||||
pd.setReadMethod(readMethod);
|
||||
try {
|
||||
pd.setWriteMethod(writeMethod);
|
||||
} catch (IntrospectionException ex) {
|
||||
logger.debug(format("Could not add write method [%s] for property [%s]. Reason: %s",
|
||||
writeMethod, propertyName, ex.getMessage()));
|
||||
// fall through -> add property descriptor as best we can
|
||||
}
|
||||
if (pd instanceof IndexedPropertyDescriptor) {
|
||||
((IndexedPropertyDescriptor)pd).setIndexedWriteMethod(null);
|
||||
((IndexedPropertyDescriptor)pd).setIndexedReadMethod(indexedReadMethod);
|
||||
try {
|
||||
((IndexedPropertyDescriptor)pd).setIndexedWriteMethod(indexedWriteMethod);
|
||||
} catch (IntrospectionException ex) {
|
||||
logger.debug(format("Could not add indexed write method [%s] for property [%s]. Reason: %s",
|
||||
indexedWriteMethod, propertyName, ex.getMessage()));
|
||||
// fall through -> add property descriptor as best we can
|
||||
}
|
||||
}
|
||||
this.propertyDescriptors.add(pd);
|
||||
throw new IllegalArgumentException(
|
||||
"write method must have exactly 1 or 2 parameters: " + method);
|
||||
}
|
||||
}
|
||||
|
||||
private String propertyNameFor(Method method) {
|
||||
return Introspector.decapitalize(method.getName().substring(3,method.getName().length()));
|
||||
private PropertyDescriptor findExistingPropertyDescriptor(
|
||||
String propertyName, Class<?> propertyType) {
|
||||
|
||||
for (PropertyDescriptor pd : this.propertyDescriptors) {
|
||||
final Class<?> candidateType;
|
||||
final String candidateName = pd.getName();
|
||||
if (pd instanceof IndexedPropertyDescriptor) {
|
||||
IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
|
||||
candidateType = ipd.getIndexedPropertyType();
|
||||
if (candidateName.equals(propertyName) &&
|
||||
(candidateType.equals(propertyType) ||
|
||||
candidateType.equals(propertyType.getComponentType()))) {
|
||||
return pd;
|
||||
}
|
||||
}
|
||||
else {
|
||||
candidateType = pd.getPropertyType();
|
||||
if (candidateName.equals(propertyName) &&
|
||||
(candidateType.equals(propertyType) ||
|
||||
propertyType.equals(candidateType.getComponentType()))) {
|
||||
return pd;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object getterMethodNameFor(String name) {
|
||||
return "get" + StringUtils.capitalize(name);
|
||||
private String propertyNameFor(Method method) {
|
||||
return Introspector.decapitalize(
|
||||
method.getName().substring(3, method.getName().length()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the set of {@link PropertyDescriptor}s from the wrapped {@link BeanInfo}
|
||||
* object as well as {@code PropertyDescriptor}s for each non-void returning setter
|
||||
* method found during construction.
|
||||
* @see #ExtendedBeanInfo(BeanInfo)
|
||||
*/
|
||||
public PropertyDescriptor[] getPropertyDescriptors() {
|
||||
return this.propertyDescriptors.toArray(
|
||||
new PropertyDescriptor[this.propertyDescriptors.size()]);
|
||||
}
|
||||
|
||||
public BeanInfo[] getAdditionalBeanInfo() {
|
||||
@@ -353,45 +244,431 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
return delegate.getEventSetDescriptors();
|
||||
}
|
||||
|
||||
public Image getIcon(int arg0) {
|
||||
return delegate.getIcon(arg0);
|
||||
public Image getIcon(int iconKind) {
|
||||
return delegate.getIcon(iconKind);
|
||||
}
|
||||
|
||||
public MethodDescriptor[] getMethodDescriptors() {
|
||||
return delegate.getMethodDescriptors();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of {@link PropertyDescriptor}s from the wrapped {@link BeanInfo}
|
||||
* object as well as {@code PropertyDescriptor}s for each non-void returning setter
|
||||
* method found during construction.
|
||||
* @see #ExtendedBeanInfo(BeanInfo)
|
||||
*/
|
||||
public PropertyDescriptor[] getPropertyDescriptors() {
|
||||
return this.propertyDescriptors.toArray(new PropertyDescriptor[this.propertyDescriptors.size()]);
|
||||
|
||||
class SimpleNonIndexedPropertyDescriptor extends PropertyDescriptor {
|
||||
|
||||
private Method readMethod;
|
||||
private Method writeMethod;
|
||||
private Class<?> propertyType;
|
||||
private Class<?> propertyEditorClass;
|
||||
|
||||
|
||||
public SimpleNonIndexedPropertyDescriptor(PropertyDescriptor original)
|
||||
throws IntrospectionException {
|
||||
|
||||
this(original.getName(), original.getReadMethod(), original.getWriteMethod());
|
||||
copyNonMethodProperties(original, this);
|
||||
}
|
||||
|
||||
public SimpleNonIndexedPropertyDescriptor(String propertyName,
|
||||
Method readMethod, Method writeMethod) throws IntrospectionException {
|
||||
|
||||
super(propertyName, null, null);
|
||||
this.setReadMethod(readMethod);
|
||||
this.setWriteMethod(writeMethod);
|
||||
this.propertyType = findPropertyType(readMethod, writeMethod);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sorts PropertyDescriptor instances alpha-numerically to emulate the behavior of
|
||||
* {@link java.beans.BeanInfo#getPropertyDescriptors()}.
|
||||
*
|
||||
* @see ExtendedBeanInfo#propertyDescriptors
|
||||
*/
|
||||
static class PropertyDescriptorComparator implements Comparator<PropertyDescriptor> {
|
||||
public int compare(PropertyDescriptor desc1, PropertyDescriptor desc2) {
|
||||
String left = desc1.getName();
|
||||
String right = desc2.getName();
|
||||
for (int i = 0; i < left.length(); i++) {
|
||||
if (right.length() == i) {
|
||||
return 1;
|
||||
}
|
||||
int result = left.getBytes()[i] - right.getBytes()[i];
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public Method getReadMethod() {
|
||||
return this.readMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadMethod(Method readMethod) {
|
||||
this.readMethod = readMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getWriteMethod() {
|
||||
return this.writeMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteMethod(Method writeMethod) {
|
||||
this.writeMethod = writeMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPropertyType() {
|
||||
if (this.propertyType == null) {
|
||||
try {
|
||||
this.propertyType = findPropertyType(this.readMethod, this.writeMethod);
|
||||
} catch (IntrospectionException ex) {
|
||||
// ignore, as does PropertyDescriptor#getPropertyType
|
||||
}
|
||||
return left.length() - right.length();
|
||||
}
|
||||
return this.propertyType;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPropertyEditorClass() {
|
||||
return this.propertyEditorClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPropertyEditorClass(Class<?> propertyEditorClass) {
|
||||
this.propertyEditorClass = propertyEditorClass;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return PropertyDescriptorUtils.equals(this, obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s[name=%s, propertyType=%s, readMethod=%s, writeMethod=%s]",
|
||||
this.getClass().getSimpleName(), this.getName(), this.getPropertyType(),
|
||||
this.readMethod, this.writeMethod);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SimpleIndexedPropertyDescriptor extends IndexedPropertyDescriptor {
|
||||
|
||||
private Method readMethod;
|
||||
private Method writeMethod;
|
||||
private Class<?> propertyType;
|
||||
private Class<?> propertyEditorClass;
|
||||
|
||||
private Method indexedReadMethod;
|
||||
private Method indexedWriteMethod;
|
||||
private Class<?> indexedPropertyType;
|
||||
|
||||
|
||||
public SimpleIndexedPropertyDescriptor(IndexedPropertyDescriptor original)
|
||||
throws IntrospectionException {
|
||||
|
||||
this(original.getName(), original.getReadMethod(), original.getWriteMethod(),
|
||||
original.getIndexedReadMethod(), original.getIndexedWriteMethod());
|
||||
copyNonMethodProperties(original, this);
|
||||
}
|
||||
|
||||
public SimpleIndexedPropertyDescriptor(String propertyName,
|
||||
Method readMethod, Method writeMethod,
|
||||
Method indexedReadMethod, Method indexedWriteMethod)
|
||||
throws IntrospectionException {
|
||||
|
||||
super(propertyName, null, null, null, null);
|
||||
this.setReadMethod(readMethod);
|
||||
this.setWriteMethod(writeMethod);
|
||||
this.propertyType = findPropertyType(readMethod, writeMethod);
|
||||
|
||||
this.setIndexedReadMethod(indexedReadMethod);
|
||||
this.setIndexedWriteMethod(indexedWriteMethod);
|
||||
this.indexedPropertyType = findIndexedPropertyType(
|
||||
this.getName(), this.propertyType, indexedReadMethod, indexedWriteMethod);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Method getReadMethod() {
|
||||
return this.readMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadMethod(Method readMethod) {
|
||||
this.readMethod = readMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getWriteMethod() {
|
||||
return this.writeMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteMethod(Method writeMethod) {
|
||||
this.writeMethod = writeMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPropertyType() {
|
||||
if (this.propertyType == null) {
|
||||
try {
|
||||
this.propertyType = findPropertyType(this.readMethod, this.writeMethod);
|
||||
} catch (IntrospectionException ex) {
|
||||
// ignore, as does IndexedPropertyDescriptor#getPropertyType
|
||||
}
|
||||
}
|
||||
return this.propertyType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getIndexedReadMethod() {
|
||||
return this.indexedReadMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIndexedReadMethod(Method indexedReadMethod) throws IntrospectionException {
|
||||
this.indexedReadMethod = indexedReadMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getIndexedWriteMethod() {
|
||||
return this.indexedWriteMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIndexedWriteMethod(Method indexedWriteMethod) throws IntrospectionException {
|
||||
this.indexedWriteMethod = indexedWriteMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getIndexedPropertyType() {
|
||||
if (this.indexedPropertyType == null) {
|
||||
try {
|
||||
this.indexedPropertyType = findIndexedPropertyType(
|
||||
this.getName(), this.getPropertyType(),
|
||||
this.indexedReadMethod, this.indexedWriteMethod);
|
||||
} catch (IntrospectionException ex) {
|
||||
// ignore, as does IndexedPropertyDescriptor#getIndexedPropertyType
|
||||
}
|
||||
}
|
||||
return this.indexedPropertyType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPropertyEditorClass() {
|
||||
return this.propertyEditorClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPropertyEditorClass(Class<?> propertyEditorClass) {
|
||||
this.propertyEditorClass = propertyEditorClass;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @see java.beans.IndexedPropertyDescriptor#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj != null && obj instanceof IndexedPropertyDescriptor) {
|
||||
IndexedPropertyDescriptor other = (IndexedPropertyDescriptor) obj;
|
||||
if (!compareMethods(getIndexedReadMethod(), other.getIndexedReadMethod())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!compareMethods(getIndexedWriteMethod(), other.getIndexedWriteMethod())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getIndexedPropertyType() != other.getIndexedPropertyType()) {
|
||||
return false;
|
||||
}
|
||||
return PropertyDescriptorUtils.equals(this, obj);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s[name=%s, propertyType=%s, indexedPropertyType=%s, " +
|
||||
"readMethod=%s, writeMethod=%s, indexedReadMethod=%s, indexedWriteMethod=%s]",
|
||||
this.getClass().getSimpleName(), this.getName(), this.getPropertyType(),
|
||||
this.getIndexedPropertyType(), this.readMethod, this.writeMethod,
|
||||
this.indexedReadMethod, this.indexedWriteMethod);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PropertyDescriptorUtils {
|
||||
|
||||
/*
|
||||
* see java.beans.FeatureDescriptor#FeatureDescriptor(FeatureDescriptor)
|
||||
*/
|
||||
public static void copyNonMethodProperties(PropertyDescriptor source, PropertyDescriptor target)
|
||||
throws IntrospectionException {
|
||||
|
||||
target.setExpert(source.isExpert());
|
||||
target.setHidden(source.isHidden());
|
||||
target.setPreferred(source.isPreferred());
|
||||
target.setName(source.getName());
|
||||
target.setShortDescription(source.getShortDescription());
|
||||
target.setDisplayName(source.getDisplayName());
|
||||
|
||||
// copy all attributes (emulating behavior of private FeatureDescriptor#addTable)
|
||||
Enumeration<String> keys = source.attributeNames();
|
||||
while (keys.hasMoreElements()) {
|
||||
String key = keys.nextElement();
|
||||
target.setValue(key, source.getValue(key));
|
||||
}
|
||||
|
||||
// see java.beans.PropertyDescriptor#PropertyDescriptor(PropertyDescriptor)
|
||||
target.setPropertyEditorClass(source.getPropertyEditorClass());
|
||||
target.setBound(source.isBound());
|
||||
target.setConstrained(source.isConstrained());
|
||||
}
|
||||
|
||||
/*
|
||||
* See PropertyDescriptor#findPropertyType
|
||||
*/
|
||||
public static Class<?> findPropertyType(Method readMethod, Method writeMethod)
|
||||
throws IntrospectionException {
|
||||
|
||||
Class<?> propertyType = null;
|
||||
|
||||
if (readMethod != null) {
|
||||
Class<?>[] params = readMethod.getParameterTypes();
|
||||
if (params.length != 0) {
|
||||
throw new IntrospectionException("bad read method arg count: " + readMethod);
|
||||
}
|
||||
propertyType = readMethod.getReturnType();
|
||||
if (propertyType == Void.TYPE) {
|
||||
throw new IntrospectionException("read method "
|
||||
+ readMethod.getName() + " returns void");
|
||||
}
|
||||
}
|
||||
if (writeMethod != null) {
|
||||
Class<?> params[] = writeMethod.getParameterTypes();
|
||||
if (params.length != 1) {
|
||||
throw new IntrospectionException("bad write method arg count: " + writeMethod);
|
||||
}
|
||||
if (propertyType != null
|
||||
&& !params[0].isAssignableFrom(propertyType)) {
|
||||
throw new IntrospectionException("type mismatch between read and write methods");
|
||||
}
|
||||
propertyType = params[0];
|
||||
}
|
||||
return propertyType;
|
||||
}
|
||||
|
||||
/*
|
||||
* See IndexedPropertyDescriptor#findIndexedPropertyType
|
||||
*/
|
||||
public static Class<?> findIndexedPropertyType(String name, Class<?> propertyType,
|
||||
Method indexedReadMethod, Method indexedWriteMethod)
|
||||
throws IntrospectionException {
|
||||
|
||||
Class<?> indexedPropertyType = null;
|
||||
|
||||
if (indexedReadMethod != null) {
|
||||
Class<?> params[] = indexedReadMethod.getParameterTypes();
|
||||
if (params.length != 1) {
|
||||
throw new IntrospectionException(
|
||||
"bad indexed read method arg count");
|
||||
}
|
||||
if (params[0] != Integer.TYPE) {
|
||||
throw new IntrospectionException(
|
||||
"non int index to indexed read method");
|
||||
}
|
||||
indexedPropertyType = indexedReadMethod.getReturnType();
|
||||
if (indexedPropertyType == Void.TYPE) {
|
||||
throw new IntrospectionException(
|
||||
"indexed read method returns void");
|
||||
}
|
||||
}
|
||||
if (indexedWriteMethod != null) {
|
||||
Class<?> params[] = indexedWriteMethod.getParameterTypes();
|
||||
if (params.length != 2) {
|
||||
throw new IntrospectionException(
|
||||
"bad indexed write method arg count");
|
||||
}
|
||||
if (params[0] != Integer.TYPE) {
|
||||
throw new IntrospectionException(
|
||||
"non int index to indexed write method");
|
||||
}
|
||||
if (indexedPropertyType != null && indexedPropertyType != params[1]) {
|
||||
throw new IntrospectionException(
|
||||
"type mismatch between indexed read and indexed write methods: " + name);
|
||||
}
|
||||
indexedPropertyType = params[1];
|
||||
}
|
||||
if (propertyType != null
|
||||
&& (!propertyType.isArray() ||
|
||||
propertyType.getComponentType() != indexedPropertyType)) {
|
||||
throw new IntrospectionException(
|
||||
"type mismatch between indexed and non-indexed methods: " + name);
|
||||
}
|
||||
return indexedPropertyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the given {@link PropertyDescriptor} against the given {@link Object} and
|
||||
* return {@code true} if they are objects are equivalent, i.e. both are {@code
|
||||
* PropertyDescriptor}s whose read method, write method, property types, property
|
||||
* editor and flags are equivalent.
|
||||
*
|
||||
* @see PropertyDescriptor#equals(Object)
|
||||
*/
|
||||
public static boolean equals(PropertyDescriptor pd1, Object obj) {
|
||||
if (pd1 == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj != null && obj instanceof PropertyDescriptor) {
|
||||
PropertyDescriptor pd2 = (PropertyDescriptor) obj;
|
||||
if (!compareMethods(pd1.getReadMethod(), pd2.getReadMethod())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!compareMethods(pd1.getWriteMethod(), pd2.getWriteMethod())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pd1.getPropertyType() == pd2.getPropertyType()
|
||||
&& pd1.getPropertyEditorClass() == pd2.getPropertyEditorClass()
|
||||
&& pd1.isBound() == pd2.isBound()
|
||||
&& pd1.isConstrained() == pd2.isConstrained()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* see PropertyDescriptor#compareMethods
|
||||
*/
|
||||
public static boolean compareMethods(Method a, Method b) {
|
||||
if ((a == null) != (b == null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a != null && b != null) {
|
||||
if (!a.equals(b)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sorts PropertyDescriptor instances alpha-numerically to emulate the behavior of
|
||||
* {@link java.beans.BeanInfo#getPropertyDescriptors()}.
|
||||
*
|
||||
* @see ExtendedBeanInfo#propertyDescriptors
|
||||
*/
|
||||
class PropertyDescriptorComparator implements Comparator<PropertyDescriptor> {
|
||||
|
||||
public int compare(PropertyDescriptor desc1, PropertyDescriptor desc2) {
|
||||
String left = desc1.getName();
|
||||
String right = desc2.getName();
|
||||
for (int i = 0; i < left.length(); i++) {
|
||||
if (right.length() == i) {
|
||||
return 1;
|
||||
}
|
||||
int result = left.getBytes()[i] - right.getBytes()[i];
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return left.length() - right.length();
|
||||
}
|
||||
}
|
||||
|
||||
+6
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.beans.factory.access;
|
||||
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
|
||||
/**
|
||||
@@ -34,7 +33,7 @@ public interface BeanFactoryReference {
|
||||
|
||||
/**
|
||||
* Return the {@link BeanFactory} instance held by this reference.
|
||||
* @throws IllegalStateException if invoked after <code>release()</code> has been called
|
||||
* @throws IllegalStateException if invoked after {@code release()} has been called
|
||||
*/
|
||||
BeanFactory getFactory();
|
||||
|
||||
@@ -42,18 +41,17 @@ public interface BeanFactoryReference {
|
||||
* Indicate that the {@link BeanFactory} instance referred to by this object is not
|
||||
* needed any longer by the client code which obtained the {@link BeanFactoryReference}.
|
||||
* <p>Depending on the actual implementation of {@link BeanFactoryLocator}, and
|
||||
* the actual type of <code>BeanFactory</code>, this may possibly not actually
|
||||
* do anything; alternately in the case of a 'closeable' <code>BeanFactory</code>
|
||||
* the actual type of {@code BeanFactory}, this may possibly not actually
|
||||
* do anything; alternately in the case of a 'closeable' {@code BeanFactory}
|
||||
* or derived class (such as {@link org.springframework.context.ApplicationContext})
|
||||
* may 'close' it, or may 'close' it once no more references remain.
|
||||
* <p>In an EJB usage scenario this would normally be called from
|
||||
* <code>ejbRemove()</code> and <code>ejbPassivate()</code>.
|
||||
* {@code ejbRemove()} and {@code ejbPassivate()}.
|
||||
* <p>This is safe to call multiple times.
|
||||
* @throws FatalBeanException if the <code>BeanFactory</code> cannot be released
|
||||
* @see BeanFactoryLocator
|
||||
* @see org.springframework.context.access.ContextBeanFactoryReference
|
||||
* @see org.springframework.context.ConfigurableApplicationContext#close()
|
||||
*/
|
||||
void release() throws FatalBeanException;
|
||||
void release();
|
||||
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -265,7 +265,7 @@ public class InitDestroyAnnotationBeanPostProcessor
|
||||
}
|
||||
|
||||
public void checkConfigMembers(RootBeanDefinition beanDefinition) {
|
||||
synchronized(this.initMethods) {
|
||||
synchronized (this.initMethods) {
|
||||
for (Iterator<LifecycleElement> it = this.initMethods.iterator(); it.hasNext();) {
|
||||
String methodIdentifier = it.next().getIdentifier();
|
||||
if (!beanDefinition.isExternallyManagedInitMethod(methodIdentifier)) {
|
||||
@@ -276,7 +276,7 @@ public class InitDestroyAnnotationBeanPostProcessor
|
||||
}
|
||||
}
|
||||
}
|
||||
synchronized(this.destroyMethods) {
|
||||
synchronized (this.destroyMethods) {
|
||||
for (Iterator<LifecycleElement> it = this.destroyMethods.iterator(); it.hasNext();) {
|
||||
String methodIdentifier = it.next().getIdentifier();
|
||||
if (!beanDefinition.isExternallyManagedDestroyMethod(methodIdentifier)) {
|
||||
|
||||
-202
@@ -1,202 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.beans.factory.config;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* A container object holding a map of attributes and optionally destruction callbacks. The callbacks will be invoked,
|
||||
* if an attribute is being removed or if the holder is cleaned out.
|
||||
*
|
||||
* @author Micha Kiener
|
||||
* @since 3.1
|
||||
*/
|
||||
public class DestructionAwareAttributeHolder implements Serializable {
|
||||
|
||||
/** The map containing the registered attributes. */
|
||||
private final Map<String, Object> attributes = new ConcurrentHashMap<String, Object>();
|
||||
|
||||
/**
|
||||
* The optional map having any destruction callbacks registered using the
|
||||
* name of the bean as the key.
|
||||
*/
|
||||
private Map<String, Runnable> registeredDestructionCallbacks;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the map representation of the registered attributes directly. Be
|
||||
* aware to synchronize any invocations to it on the map object itself to
|
||||
* avoid concurrent modification exceptions.
|
||||
*
|
||||
* @return the attributes as a map representation
|
||||
*/
|
||||
public Map<String, Object> getAttributeMap() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the attribute having the specified name, if available,
|
||||
* <code>null</code> otherwise.
|
||||
*
|
||||
* @param name
|
||||
* the name of the attribute to be returned
|
||||
* @return the attribute value or <code>null</code> if not available
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object getAttribute(String name) {
|
||||
return attributes.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given object with the specified name as an attribute to the
|
||||
* underlying map.
|
||||
*
|
||||
* @param name
|
||||
* the name of the attribute
|
||||
* @param value
|
||||
* the value to be stored
|
||||
* @return any previously object stored under the same name, if any,
|
||||
* <code>null</code> otherwise
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object setAttribute(String name, Object value) {
|
||||
return attributes.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the object with the given <code>name</code> from the underlying
|
||||
* scope.
|
||||
* <p>
|
||||
* Returns <code>null</code> if no object was found; otherwise returns the
|
||||
* removed <code>Object</code>.
|
||||
* <p>
|
||||
* Note that an implementation should also remove a registered destruction
|
||||
* callback for the specified object, if any. It does, however, <i>not</i>
|
||||
* need to <i>execute</i> a registered destruction callback in this case,
|
||||
* since the object will be destroyed by the caller (if appropriate).
|
||||
* <p>
|
||||
* <b>Note: This is an optional operation.</b> Implementations may throw
|
||||
* {@link UnsupportedOperationException} if they do not support explicitly
|
||||
* removing an object.
|
||||
*
|
||||
* @param name
|
||||
* the name of the object to remove
|
||||
* @return the removed object, or <code>null</code> if no object was present
|
||||
* @see #registerDestructionCallback
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object removeAttribute(String name) {
|
||||
Object value = attributes.remove(name);
|
||||
|
||||
// check for a destruction callback to be invoked
|
||||
Runnable callback = getDestructionCallback(name, true);
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the map by removing all registered attribute values and invokes
|
||||
* every destruction callback registered.
|
||||
*/
|
||||
public void clear() {
|
||||
synchronized (this) {
|
||||
// step through the attribute map and invoke destruction callbacks,
|
||||
// if any
|
||||
if (registeredDestructionCallbacks != null) {
|
||||
for (Runnable runnable : registeredDestructionCallbacks.values()) {
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
registeredDestructionCallbacks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// clear out the registered attribute map
|
||||
attributes.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be executed on destruction of the specified object
|
||||
* in the scope (or at destruction of the entire scope, if the scope does
|
||||
* not destroy individual objects but rather only terminates in its
|
||||
* entirety).
|
||||
* <p>
|
||||
* <b>Note: This is an optional operation.</b> This method will only be
|
||||
* called for scoped beans with actual destruction configuration
|
||||
* (DisposableBean, destroy-method, DestructionAwareBeanPostProcessor).
|
||||
* Implementations should do their best to execute a given callback at the
|
||||
* appropriate time. If such a callback is not supported by the underlying
|
||||
* runtime environment at all, the callback <i>must be ignored and a
|
||||
* corresponding warning should be logged</i>.
|
||||
* <p>
|
||||
* Note that 'destruction' refers to to automatic destruction of the object
|
||||
* as part of the scope's own lifecycle, not to the individual scoped object
|
||||
* having been explicitly removed by the application. If a scoped object
|
||||
* gets removed via this facade's {@link #removeAttribute(String)} method,
|
||||
* any registered destruction callback should be removed as well, assuming
|
||||
* that the removed object will be reused or manually destroyed.
|
||||
*
|
||||
* @param name
|
||||
* the name of the object to execute the destruction callback for
|
||||
* @param callback
|
||||
* the destruction callback to be executed. Note that the
|
||||
* passed-in Runnable will never throw an exception, so it can
|
||||
* safely be executed without an enclosing try-catch block.
|
||||
* Furthermore, the Runnable will usually be serializable,
|
||||
* provided that its target object is serializable as well.
|
||||
* @see org.springframework.beans.factory.DisposableBean
|
||||
* @see org.springframework.beans.factory.support.AbstractBeanDefinition#getDestroyMethodName()
|
||||
* @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor
|
||||
*/
|
||||
public void registerDestructionCallback(String name, Runnable callback) {
|
||||
if (registeredDestructionCallbacks == null) {
|
||||
registeredDestructionCallbacks = new ConcurrentHashMap<String, Runnable>();
|
||||
}
|
||||
|
||||
registeredDestructionCallbacks.put(name, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the destruction callback, if any registered for the attribute
|
||||
* with the given name or <code>null</code> if no such callback was
|
||||
* registered.
|
||||
*
|
||||
* @param name
|
||||
* the name of the registered callback requested
|
||||
* @param remove
|
||||
* <code>true</code>, if the callback should be removed after
|
||||
* this call, <code>false</code>, if it stays
|
||||
* @return the callback, if found, <code>null</code> otherwise
|
||||
*/
|
||||
public Runnable getDestructionCallback(String name, boolean remove) {
|
||||
if (registeredDestructionCallbacks == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
return registeredDestructionCallbacks.remove(name);
|
||||
}
|
||||
|
||||
return registeredDestructionCallbacks.get(name);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -101,7 +101,7 @@ public class ObjectFactoryCreatingFactoryBean extends AbstractFactoryBean<Object
|
||||
|
||||
/**
|
||||
* Set the name of the target bean.
|
||||
* <p>The target does not <i>have</> to be a non-singleton bean, but realisticially
|
||||
* <p>The target does not <i>have</i> to be a non-singleton bean, but realistically
|
||||
* always will be (because if the target bean were a singleton, then said singleton
|
||||
* bean could simply be injected straight into the dependent object, thus obviating
|
||||
* the need for the extra level of indirection afforded by this factory approach).
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -46,7 +46,7 @@ public class ProviderCreatingFactoryBean extends AbstractFactoryBean<Provider> {
|
||||
|
||||
/**
|
||||
* Set the name of the target bean.
|
||||
* <p>The target does not <i>have</> to be a non-singleton bean, but realisticially
|
||||
* <p>The target does not <i>have</i> to be a non-singleton bean, but realistically
|
||||
* always will be (because if the target bean were a singleton, then said singleton
|
||||
* bean could simply be injected straight into the dependent object, thus obviating
|
||||
* the need for the extra level of indirection afforded by this factory approach).
|
||||
|
||||
+17
-18
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,7 +27,6 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
@@ -36,8 +35,8 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link FactoryBean} implementation that takes an interface which must have one or more
|
||||
* methods with the signatures <code>MyType xxx()</code> or <code>MyType xxx(MyIdType id)</code>
|
||||
* (typically, <code>MyService getService()</code> or <code>MyService getService(String id)</code>)
|
||||
* methods with the signatures {@code MyType xxx()} or {@code MyType xxx(MyIdType id)}
|
||||
* (typically, {@code MyService getService()} or {@code MyService getService(String id)})
|
||||
* and creates a dynamic proxy which implements that interface, delegating to an
|
||||
* underlying {@link org.springframework.beans.factory.BeanFactory}.
|
||||
*
|
||||
@@ -51,7 +50,7 @@ import org.springframework.util.StringUtils;
|
||||
* setter or constructor injection of the target bean is preferable.</b>
|
||||
*
|
||||
* <p>On invocation of the no-arg factory method, or the single-arg factory
|
||||
* method with a String id of <code>null</code> or empty String, if exactly
|
||||
* method with a String id of {@code null} or empty String, if exactly
|
||||
* <b>one</b> bean in the factory matches the return type of the factory
|
||||
* method, that bean is returned, otherwise a
|
||||
* {@link org.springframework.beans.factory.NoSuchBeanDefinitionException}
|
||||
@@ -64,7 +63,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* <p>A factory method argument will usually be a String, but can also be an
|
||||
* int or a custom enumeration type, for example, stringified via
|
||||
* <code>toString</code>. The resulting String can be used as bean name as-is,
|
||||
* {@code toString}. The resulting String can be used as bean name as-is,
|
||||
* provided that corresponding beans are defined in the bean factory.
|
||||
* Alternatively, {@link #setServiceMappings(java.util.Properties) a custom mapping}
|
||||
* between service ids and bean names can be defined.
|
||||
@@ -99,7 +98,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
*</beans></pre>
|
||||
*
|
||||
* <p>The attendant <code>MyClientBean</code> class implementation might then
|
||||
* <p>The attendant {@code MyClientBean} class implementation might then
|
||||
* look something like this:
|
||||
*
|
||||
* <pre class="code">package a.b.c;
|
||||
@@ -151,7 +150,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
*</beans></pre>
|
||||
*
|
||||
* <p>The attendant <code>MyClientBean</code> class implementation might then
|
||||
* <p>The attendant {@code MyClientBean} class implementation might then
|
||||
* look something like this:
|
||||
*
|
||||
* <pre class="code">package a.b.c;
|
||||
@@ -202,8 +201,8 @@ public class ServiceLocatorFactoryBean implements FactoryBean<Object>, BeanFacto
|
||||
|
||||
/**
|
||||
* Set the service locator interface to use, which must have one or more methods with
|
||||
* the signatures <code>MyType xxx()</code> or <code>MyType xxx(MyIdType id)</code>
|
||||
* (typically, <code>MyService getService()</code> or <code>MyService getService(String id)</code>).
|
||||
* the signatures {@code MyType xxx()} or {@code MyType xxx(MyIdType id)}
|
||||
* (typically, {@code MyService getService()} or {@code MyService getService(String id)}).
|
||||
* See the {@link ServiceLocatorFactoryBean class-level Javadoc} for
|
||||
* information on the semantics of such methods.
|
||||
*/
|
||||
@@ -214,8 +213,8 @@ public class ServiceLocatorFactoryBean implements FactoryBean<Object>, BeanFacto
|
||||
/**
|
||||
* Set the exception class that the service locator should throw if service
|
||||
* lookup failed. The specified exception class must have a constructor
|
||||
* with one of the following parameter types: <code>(String, Throwable)</code>
|
||||
* or <code>(Throwable)</code> or <code>(String)</code>.
|
||||
* with one of the following parameter types: {@code (String, Throwable)}
|
||||
* or {@code (Throwable)} or {@code (String)}.
|
||||
* <p>If not specified, subclasses of Spring's BeansException will be thrown,
|
||||
* for example NoSuchBeanDefinitionException. As those are unchecked, the
|
||||
* caller does not need to handle them, so it might be acceptable that
|
||||
@@ -236,7 +235,7 @@ public class ServiceLocatorFactoryBean implements FactoryBean<Object>, BeanFacto
|
||||
* Set mappings between service ids (passed into the service locator)
|
||||
* and bean names (in the bean factory). Service ids that are not defined
|
||||
* here will be treated as bean names as-is.
|
||||
* <p>The empty string as service id key defines the mapping for <code>null</code> and
|
||||
* <p>The empty string as service id key defines the mapping for {@code null} and
|
||||
* empty string, and for factory methods without parameter. If not defined,
|
||||
* a single matching bean will be retrieved from the bean factory.
|
||||
* @param serviceMappings mappings between service ids and bean names,
|
||||
@@ -271,8 +270,8 @@ public class ServiceLocatorFactoryBean implements FactoryBean<Object>, BeanFacto
|
||||
* Determine the constructor to use for the given service locator exception
|
||||
* class. Only called in case of a custom service locator exception.
|
||||
* <p>The default implementation looks for a constructor with one of the
|
||||
* following parameter types: <code>(String, Throwable)</code>
|
||||
* or <code>(Throwable)</code> or <code>(String)</code>.
|
||||
* following parameter types: {@code (String, Throwable)}
|
||||
* or {@code (Throwable)} or {@code (String)}.
|
||||
* @param exceptionClass the exception class
|
||||
* @return the constructor to use
|
||||
* @see #setServiceLocatorExceptionClass
|
||||
@@ -364,12 +363,12 @@ public class ServiceLocatorFactoryBean implements FactoryBean<Object>, BeanFacto
|
||||
try {
|
||||
String beanName = tryGetBeanName(args);
|
||||
if (StringUtils.hasLength(beanName)) {
|
||||
// Service locator for a specific bean name.
|
||||
// Service locator for a specific bean name
|
||||
return beanFactory.getBean(beanName, serviceLocatorMethodReturnType);
|
||||
}
|
||||
else {
|
||||
// Service locator for a bean type.
|
||||
return BeanFactoryUtils.beanOfTypeIncludingAncestors(beanFactory, serviceLocatorMethodReturnType);
|
||||
// Service locator for a bean type
|
||||
return beanFactory.getBean(serviceLocatorMethodReturnType);
|
||||
}
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
|
||||
+27
-28
@@ -576,35 +576,36 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
this.logger.info("Pre-instantiating singletons in " + this);
|
||||
}
|
||||
List<String> beanNames;
|
||||
synchronized (this.beanDefinitionMap) {
|
||||
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
|
||||
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
|
||||
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
|
||||
for (String beanName : beanNames) {
|
||||
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
|
||||
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
|
||||
if (isFactoryBean(beanName)) {
|
||||
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
|
||||
boolean isEagerInit;
|
||||
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
|
||||
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
|
||||
public Boolean run() {
|
||||
return ((SmartFactoryBean<?>) factory).isEagerInit();
|
||||
}
|
||||
}, getAccessControlContext());
|
||||
}
|
||||
else {
|
||||
isEagerInit = (factory instanceof SmartFactoryBean &&
|
||||
((SmartFactoryBean<?>) factory).isEagerInit());
|
||||
}
|
||||
if (isEagerInit) {
|
||||
getBean(beanName);
|
||||
}
|
||||
beanNames = new ArrayList<String>(this.beanDefinitionNames);
|
||||
}
|
||||
for (String beanName : beanNames) {
|
||||
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
|
||||
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
|
||||
if (isFactoryBean(beanName)) {
|
||||
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
|
||||
boolean isEagerInit;
|
||||
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
|
||||
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
|
||||
public Boolean run() {
|
||||
return ((SmartFactoryBean<?>) factory).isEagerInit();
|
||||
}
|
||||
}, getAccessControlContext());
|
||||
}
|
||||
else {
|
||||
isEagerInit = (factory instanceof SmartFactoryBean &&
|
||||
((SmartFactoryBean<?>) factory).isEagerInit());
|
||||
}
|
||||
if (isEagerInit) {
|
||||
getBean(beanName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
getBean(beanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -650,9 +651,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
this.frozenBeanDefinitionNames = null;
|
||||
}
|
||||
this.beanDefinitionMap.put(beanName, beanDefinition);
|
||||
|
||||
resetBeanDefinition(beanName);
|
||||
}
|
||||
|
||||
resetBeanDefinition(beanName);
|
||||
}
|
||||
|
||||
public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
|
||||
@@ -668,9 +669,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
this.beanDefinitionNames.remove(beanName);
|
||||
this.frozenBeanDefinitionNames = null;
|
||||
|
||||
resetBeanDefinition(beanName);
|
||||
}
|
||||
|
||||
resetBeanDefinition(beanName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -685,9 +686,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
// Remove corresponding bean from singleton cache, if any. Shouldn't usually
|
||||
// be necessary, rather just meant for overriding a context's default beans
|
||||
// (e.g. the default StaticMessageSource in a StaticApplicationContext).
|
||||
synchronized (getSingletonMutex()) {
|
||||
destroySingleton(beanName);
|
||||
}
|
||||
destroySingleton(beanName);
|
||||
|
||||
// Remove any assumptions about by-type mappings
|
||||
this.singletonBeanNamesByType.clear();
|
||||
|
||||
+6
-5
@@ -98,7 +98,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
private final Set<String> singletonsCurrentlyInCreation = Collections.synchronizedSet(new HashSet<String>());
|
||||
|
||||
/** Names of beans currently excluded from in creation checks */
|
||||
private final Set<String> inCreationCheckExclusions = new HashSet<String>();
|
||||
private final Set<String> inCreationCheckExclusions = Collections.synchronizedSet(new HashSet<String>());
|
||||
|
||||
/** List of suppressed Exceptions, available for associating related causes */
|
||||
private Set<Exception> suppressedExceptions;
|
||||
@@ -437,11 +437,12 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
this.singletonsCurrentlyInDestruction = true;
|
||||
}
|
||||
|
||||
String[] disposableBeanNames;
|
||||
synchronized (this.disposableBeans) {
|
||||
String[] disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
|
||||
for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
|
||||
destroySingleton(disposableBeanNames[i]);
|
||||
}
|
||||
disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
|
||||
}
|
||||
for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
|
||||
destroySingleton(disposableBeanNames[i]);
|
||||
}
|
||||
|
||||
this.containedBeanMap.clear();
|
||||
|
||||
+18
@@ -1541,6 +1541,24 @@ public final class BeanWrapperTests {
|
||||
assertEquals(TestEnum.TEST_VALUE, consumer.getEnumValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cornerSpr10115() {
|
||||
Spr10115Bean foo = new Spr10115Bean();
|
||||
BeanWrapperImpl bwi = new BeanWrapperImpl();
|
||||
bwi.setWrappedInstance(foo);
|
||||
bwi.setPropertyValue("prop1", "val1");
|
||||
assertEquals("val1", Spr10115Bean.prop1);
|
||||
}
|
||||
|
||||
|
||||
static class Spr10115Bean {
|
||||
private static String prop1;
|
||||
|
||||
public static void setProp1(String prop1) {
|
||||
Spr10115Bean.prop1 = prop1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class Foo {
|
||||
|
||||
|
||||
+14
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
package org.springframework.beans;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.junit.Test;
|
||||
import test.beans.TestBean;
|
||||
|
||||
import org.springframework.core.OverridingClassLoader;
|
||||
|
||||
import test.beans.TestBean;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
@@ -30,7 +32,7 @@ import test.beans.TestBean;
|
||||
public final class CachedIntrospectionResultsTests {
|
||||
|
||||
@Test
|
||||
public void testAcceptClassLoader() throws Exception {
|
||||
public void acceptAndClearClassLoader() throws Exception {
|
||||
BeanWrapper bw = new BeanWrapperImpl(TestBean.class);
|
||||
assertTrue(bw.isWritableProperty("name"));
|
||||
assertTrue(bw.isWritableProperty("age"));
|
||||
@@ -50,4 +52,12 @@ public final class CachedIntrospectionResultsTests {
|
||||
assertTrue(CachedIntrospectionResults.classCache.containsKey(TestBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearClassLoaderForSystemClassLoader() throws Exception {
|
||||
BeanUtils.getPropertyDescriptors(ArrayList.class);
|
||||
assertTrue(CachedIntrospectionResults.classCache.containsKey(ArrayList.class));
|
||||
CachedIntrospectionResults.clearClassLoader(ArrayList.class.getClassLoader());
|
||||
assertFalse(CachedIntrospectionResults.classCache.containsKey(ArrayList.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+154
-35
@@ -23,23 +23,24 @@ import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.ExtendedBeanInfo.PropertyDescriptorComparator;
|
||||
import org.springframework.core.JdkVersion;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import test.beans.TestBean;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ExtendedBeanInfo}.
|
||||
*
|
||||
@@ -128,12 +129,32 @@ public class ExtendedBeanInfoTests {
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void standardReadAndNonStandardIndexedWriteMethod() throws IntrospectionException {
|
||||
@SuppressWarnings("unused") class C {
|
||||
public String[] getFoo() { return null; }
|
||||
public C setFoo(int i, String foo) { return this; }
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "foo"), is(trueUntilJdk17()));
|
||||
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(ebi, "foo"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void standardReadMethodsAndOverloadedNonStandardWriteMethods() throws Exception {
|
||||
@SuppressWarnings("unused") class C {
|
||||
@@ -150,7 +171,7 @@ public class ExtendedBeanInfoTests {
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(trueUntilJdk17()));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
|
||||
@@ -225,7 +246,7 @@ public class ExtendedBeanInfoTests {
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
|
||||
@@ -268,13 +289,13 @@ public class ExtendedBeanInfoTests {
|
||||
assertThat(hasReadMethodForProperty(bi, "bar"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "bar"), is(false));
|
||||
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "bar"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "bar"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "bar"), is(false));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
|
||||
@@ -291,7 +312,7 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
@@ -312,7 +333,7 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
@@ -329,7 +350,7 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(false));
|
||||
@@ -350,7 +371,7 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
@@ -367,7 +388,7 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(false));
|
||||
@@ -508,15 +529,14 @@ public class ExtendedBeanInfoTests {
|
||||
BeanInfo ebi = new ExtendedBeanInfo(Introspector.getBeanInfo(C.class));
|
||||
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foos"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foos"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(trueUntilJdk17()));
|
||||
|
||||
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foos"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(true));
|
||||
}
|
||||
|
||||
@Ignore // see comments at SPR-9702
|
||||
@Test
|
||||
public void cornerSpr9702() throws IntrospectionException {
|
||||
{ // baseline with standard write method
|
||||
@@ -563,6 +583,20 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prior to SPR-10111 (a follow-up fix for SPR-9702), this method would throw an
|
||||
* IntrospectionException regarding a "type mismatch between indexed and non-indexed
|
||||
* methods" intermittently (approximately one out of every four times) under JDK 7
|
||||
* due to non-deterministic results from {@link Class#getDeclaredMethods()}.
|
||||
* See http://bugs.sun.com/view_bug.do?bug_id=7023180
|
||||
* @see #cornerSpr9702()
|
||||
*/
|
||||
@Test
|
||||
public void cornerSpr10111() throws Exception {
|
||||
new ExtendedBeanInfo(Introspector.getBeanInfo(BigDecimal.class));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void subclassWriteMethodWithCovariantReturnType() throws IntrospectionException {
|
||||
@SuppressWarnings("unused") class B {
|
||||
@@ -579,10 +613,10 @@ public class ExtendedBeanInfoTests {
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
|
||||
@@ -598,7 +632,7 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(true));
|
||||
@@ -619,9 +653,67 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(ebi.getPropertyDescriptors(), equalTo(bi.getPropertyDescriptors()));
|
||||
assertThat(ebi.getPropertyDescriptors().length, equalTo(bi.getPropertyDescriptors().length));
|
||||
assertThat(ebi.getPropertyDescriptors().length, equalTo(1));
|
||||
assertThat(ebi.getPropertyDescriptors()[0].getName(), equalTo("class"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overloadedNonStandardWriteMethodsOnly_orderA() throws IntrospectionException, SecurityException, NoSuchMethodException {
|
||||
@SuppressWarnings("unused") class C {
|
||||
public Object setFoo(String p) { return new Object(); }
|
||||
public Object setFoo(int p) { return new Object(); }
|
||||
}
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "foo"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
|
||||
|
||||
for (PropertyDescriptor pd : ebi.getPropertyDescriptors()) {
|
||||
if (pd.getName().equals("foo")) {
|
||||
assertThat(pd.getWriteMethod(), is(C.class.getMethod("setFoo", String.class)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail("never matched write method");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overloadedNonStandardWriteMethodsOnly_orderB() throws IntrospectionException, SecurityException, NoSuchMethodException {
|
||||
@SuppressWarnings("unused") class C {
|
||||
public Object setFoo(int p) { return new Object(); }
|
||||
public Object setFoo(String p) { return new Object(); }
|
||||
}
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "foo"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
|
||||
|
||||
for (PropertyDescriptor pd : ebi.getPropertyDescriptors()) {
|
||||
if (pd.getName().equals("foo")) {
|
||||
assertThat(pd.getWriteMethod(), is(C.class.getMethod("setFoo", String.class)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail("never matched write method");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -645,7 +737,7 @@ public class ExtendedBeanInfoTests {
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "dateFormat"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "dateFormat"), is(trueUntilJdk17()));
|
||||
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "dateFormat"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "dateFormat"), is(false));
|
||||
@@ -653,15 +745,15 @@ public class ExtendedBeanInfoTests {
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "dateFormat"), is(trueUntilJdk17()));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "dateFormat"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "dateFormat"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "dateFormat"), is(true));
|
||||
assertThat(hasIndexedReadMethodForProperty(ebi, "dateFormat"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(ebi, "dateFormat"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(ebi, "dateFormat"), is(trueUntilJdk17()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertyCountsMatch() throws IntrospectionException {
|
||||
BeanInfo bi = Introspector.getBeanInfo(TestBean.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(ebi.getPropertyDescriptors().length, equalTo(bi.getPropertyDescriptors().length));
|
||||
}
|
||||
@@ -673,7 +765,7 @@ public class ExtendedBeanInfoTests {
|
||||
public ExtendedTestBean setFoo(String s) { return this; }
|
||||
}
|
||||
BeanInfo bi = Introspector.getBeanInfo(ExtendedTestBean.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
boolean found = false;
|
||||
for (PropertyDescriptor pd : ebi.getPropertyDescriptors()) {
|
||||
@@ -692,7 +784,7 @@ public class ExtendedBeanInfoTests {
|
||||
@Test
|
||||
public void propertyDescriptorOrderIsEqual() throws IntrospectionException {
|
||||
BeanInfo bi = Introspector.getBeanInfo(TestBean.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
for (int i = 0; i < bi.getPropertyDescriptors().length; i++) {
|
||||
assertThat("element " + i + " in BeanInfo and ExtendedBeanInfo propertyDescriptor arrays do not match",
|
||||
@@ -739,7 +831,9 @@ public class ExtendedBeanInfoTests {
|
||||
private boolean hasIndexedWriteMethodForProperty(BeanInfo beanInfo, String propertyName) {
|
||||
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
|
||||
if (pd.getName().equals(propertyName)) {
|
||||
assertThat(propertyName + " property is not indexed", pd, instanceOf(IndexedPropertyDescriptor.class));
|
||||
if (!(pd instanceof IndexedPropertyDescriptor)) {
|
||||
return false;
|
||||
}
|
||||
return ((IndexedPropertyDescriptor)pd).getIndexedWriteMethod() != null;
|
||||
}
|
||||
}
|
||||
@@ -749,7 +843,9 @@ public class ExtendedBeanInfoTests {
|
||||
private boolean hasIndexedReadMethodForProperty(BeanInfo beanInfo, String propertyName) {
|
||||
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
|
||||
if (pd.getName().equals(propertyName)) {
|
||||
assertThat(propertyName + " property is not indexed", pd, instanceOf(IndexedPropertyDescriptor.class));
|
||||
if (!(pd instanceof IndexedPropertyDescriptor)) {
|
||||
return false;
|
||||
}
|
||||
return ((IndexedPropertyDescriptor)pd).getIndexedReadMethod() != null;
|
||||
}
|
||||
}
|
||||
@@ -830,7 +926,7 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
|
||||
// and now demonstrate that we've indeed fixed the problem
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "targetMethod"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "targetMethod"), is(false));
|
||||
@@ -855,7 +951,7 @@ public class ExtendedBeanInfoTests {
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "address"), is(true));
|
||||
}
|
||||
{
|
||||
ExtendedBeanInfo bi = new ExtendedBeanInfo(Introspector.getBeanInfo(A.class));
|
||||
BeanInfo bi = new ExtendedBeanInfo(Introspector.getBeanInfo(A.class));
|
||||
assertThat(hasReadMethodForProperty(bi, "address"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "address"), is(false));
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "address"), is(true));
|
||||
@@ -863,4 +959,27 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSupportStaticWriteMethod() throws IntrospectionException {
|
||||
{
|
||||
BeanInfo bi = Introspector.getBeanInfo(WithStaticWriteMethod.class);
|
||||
assertThat(hasReadMethodForProperty(bi, "prop1"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "prop1"), is(false));
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "prop1"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "prop1"), is(false));
|
||||
}
|
||||
{
|
||||
BeanInfo bi = new ExtendedBeanInfo(Introspector.getBeanInfo(WithStaticWriteMethod.class));
|
||||
assertThat(hasReadMethodForProperty(bi, "prop1"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "prop1"), is(true));
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "prop1"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "prop1"), is(false));
|
||||
}
|
||||
}
|
||||
|
||||
static class WithStaticWriteMethod {
|
||||
@SuppressWarnings("unused")
|
||||
public static void setProp1(String prop1) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+59
-53
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,6 +35,7 @@ import javax.mail.internet.MimeBodyPart;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import javax.mail.internet.MimeMultipart;
|
||||
import javax.mail.internet.MimePart;
|
||||
import javax.mail.internet.MimeUtility;
|
||||
|
||||
import org.springframework.core.io.InputStreamSource;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -241,7 +242,7 @@ public class MimeMessageHelper {
|
||||
* @see #MimeMessageHelper(javax.mail.internet.MimeMessage, int, String)
|
||||
*/
|
||||
public MimeMessageHelper(MimeMessage mimeMessage, boolean multipart, String encoding)
|
||||
throws MessagingException {
|
||||
throws MessagingException {
|
||||
|
||||
this(mimeMessage, (multipart ? MULTIPART_MODE_MIXED_RELATED : MULTIPART_MODE_NO), encoding);
|
||||
}
|
||||
@@ -283,7 +284,7 @@ public class MimeMessageHelper {
|
||||
* @see #MULTIPART_MODE_MIXED_RELATED
|
||||
*/
|
||||
public MimeMessageHelper(MimeMessage mimeMessage, int multipartMode, String encoding)
|
||||
throws MessagingException {
|
||||
throws MessagingException {
|
||||
|
||||
this.mimeMessage = mimeMessage;
|
||||
createMimeMultiparts(mimeMessage, multipartMode);
|
||||
@@ -355,7 +356,7 @@ public class MimeMessageHelper {
|
||||
/**
|
||||
* Set the given MimeMultipart objects for use by this MimeMessageHelper.
|
||||
* @param root the root MimeMultipart object, which attachments will be added to;
|
||||
* or <code>null</code> to indicate no multipart at all
|
||||
* or {@code null} to indicate no multipart at all
|
||||
* @param main the main MimeMultipart object, which text(s) and inline elements
|
||||
* will be added to (can be the same as the root multipart object, or an element
|
||||
* nested underneath the root multipart element)
|
||||
@@ -380,8 +381,8 @@ public class MimeMessageHelper {
|
||||
private void checkMultipart() throws IllegalStateException {
|
||||
if (!isMultipart()) {
|
||||
throw new IllegalStateException("Not in multipart mode - " +
|
||||
"create an appropriate MimeMessageHelper via a constructor that takes a 'multipart' flag " +
|
||||
"if you need to set alternative texts or add inline elements or attachments.");
|
||||
"create an appropriate MimeMessageHelper via a constructor that takes a 'multipart' flag " +
|
||||
"if you need to set alternative texts or add inline elements or attachments.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,7 +421,7 @@ public class MimeMessageHelper {
|
||||
* Determine the default encoding for the given MimeMessage.
|
||||
* @param mimeMessage the passed-in MimeMessage
|
||||
* @return the default encoding associated with the MimeMessage,
|
||||
* or <code>null</code> if none found
|
||||
* or {@code null} if none found
|
||||
*/
|
||||
protected String getDefaultEncoding(MimeMessage mimeMessage) {
|
||||
if (mimeMessage instanceof SmartMimeMessage) {
|
||||
@@ -456,12 +457,12 @@ public class MimeMessageHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Java Activation Framework <code>FileTypeMap</code> to use
|
||||
* Set the Java Activation Framework {@code FileTypeMap} to use
|
||||
* for determining the content type of inline content and attachments
|
||||
* that get added to the message.
|
||||
* <p>Default is the <code>FileTypeMap</code> that the underlying
|
||||
* <p>Default is the {@code FileTypeMap} that the underlying
|
||||
* MimeMessage carries, if any, or the Activation Framework's default
|
||||
* <code>FileTypeMap</code> instance else.
|
||||
* {@code FileTypeMap} instance else.
|
||||
* @see #addInline
|
||||
* @see #addAttachment
|
||||
* @see #getDefaultFileTypeMap(javax.mail.internet.MimeMessage)
|
||||
@@ -474,7 +475,7 @@ public class MimeMessageHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the <code>FileTypeMap</code> used by this MimeMessageHelper.
|
||||
* Return the {@code FileTypeMap} used by this MimeMessageHelper.
|
||||
*/
|
||||
public FileTypeMap getFileTypeMap() {
|
||||
return this.fileTypeMap;
|
||||
@@ -485,7 +486,7 @@ public class MimeMessageHelper {
|
||||
* Set whether to validate all addresses which get passed to this helper.
|
||||
* Default is "false".
|
||||
* <p>Note that this is by default just available for JavaMail >= 1.3.
|
||||
* You can override the default <code>validateAddress method</code> for
|
||||
* You can override the default {@code validateAddress method} for
|
||||
* validation on older JavaMail versions (or for custom validation).
|
||||
* @see #validateAddress
|
||||
*/
|
||||
@@ -503,7 +504,7 @@ public class MimeMessageHelper {
|
||||
/**
|
||||
* Validate the given mail address.
|
||||
* Called by all of MimeMessageHelper's address setters and adders.
|
||||
* <p>Default implementation invokes <code>InternetAddress.validate()</code>,
|
||||
* <p>Default implementation invokes {@code InternetAddress.validate()},
|
||||
* provided that address validation is activated for the helper instance.
|
||||
* <p>Note that this method will just work on JavaMail >= 1.3. You can override
|
||||
* it for validation on older JavaMail versions or for custom validation.
|
||||
@@ -546,7 +547,7 @@ public class MimeMessageHelper {
|
||||
public void setFrom(String from, String personal) throws MessagingException, UnsupportedEncodingException {
|
||||
Assert.notNull(from, "From address must not be null");
|
||||
setFrom(getEncoding() != null ?
|
||||
new InternetAddress(from, personal, getEncoding()) : new InternetAddress(from, personal));
|
||||
new InternetAddress(from, personal, getEncoding()) : new InternetAddress(from, personal));
|
||||
}
|
||||
|
||||
public void setReplyTo(InternetAddress replyTo) throws MessagingException {
|
||||
@@ -608,8 +609,8 @@ public class MimeMessageHelper {
|
||||
public void addTo(String to, String personal) throws MessagingException, UnsupportedEncodingException {
|
||||
Assert.notNull(to, "To address must not be null");
|
||||
addTo(getEncoding() != null ?
|
||||
new InternetAddress(to, personal, getEncoding()) :
|
||||
new InternetAddress(to, personal));
|
||||
new InternetAddress(to, personal, getEncoding()) :
|
||||
new InternetAddress(to, personal));
|
||||
}
|
||||
|
||||
|
||||
@@ -653,8 +654,8 @@ public class MimeMessageHelper {
|
||||
public void addCc(String cc, String personal) throws MessagingException, UnsupportedEncodingException {
|
||||
Assert.notNull(cc, "Cc address must not be null");
|
||||
addCc(getEncoding() != null ?
|
||||
new InternetAddress(cc, personal, getEncoding()) :
|
||||
new InternetAddress(cc, personal));
|
||||
new InternetAddress(cc, personal, getEncoding()) :
|
||||
new InternetAddress(cc, personal));
|
||||
}
|
||||
|
||||
|
||||
@@ -698,8 +699,8 @@ public class MimeMessageHelper {
|
||||
public void addBcc(String bcc, String personal) throws MessagingException, UnsupportedEncodingException {
|
||||
Assert.notNull(bcc, "Bcc address must not be null");
|
||||
addBcc(getEncoding() != null ?
|
||||
new InternetAddress(bcc, personal, getEncoding()) :
|
||||
new InternetAddress(bcc, personal));
|
||||
new InternetAddress(bcc, personal, getEncoding()) :
|
||||
new InternetAddress(bcc, personal));
|
||||
}
|
||||
|
||||
private InternetAddress parseAddress(String address) throws MessagingException {
|
||||
@@ -730,7 +731,7 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Set the sent-date of the message.
|
||||
* @param sentDate the date to set (never <code>null</code>)
|
||||
* @param sentDate the date to set (never {@code null})
|
||||
* @throws MessagingException in case of errors
|
||||
*/
|
||||
public void setSentDate(Date sentDate) throws MessagingException {
|
||||
@@ -758,7 +759,7 @@ public class MimeMessageHelper {
|
||||
* Set the given text directly as content in non-multipart mode
|
||||
* or as default body part in multipart mode.
|
||||
* Always applies the default content type "text/plain".
|
||||
* <p><b>NOTE:</b> Invoke {@link #addInline} <i>after</i> <code>setText</code>;
|
||||
* <p><b>NOTE:</b> Invoke {@link #addInline} <i>after</i> {@code setText};
|
||||
* else, mail readers might not be able to resolve inline references correctly.
|
||||
* @param text the text for the message
|
||||
* @throws MessagingException in case of errors
|
||||
@@ -771,7 +772,7 @@ public class MimeMessageHelper {
|
||||
* Set the given text directly as content in non-multipart mode
|
||||
* or as default body part in multipart mode.
|
||||
* The "html" flag determines the content type to apply.
|
||||
* <p><b>NOTE:</b> Invoke {@link #addInline} <i>after</i> <code>setText</code>;
|
||||
* <p><b>NOTE:</b> Invoke {@link #addInline} <i>after</i> {@code setText};
|
||||
* else, mail readers might not be able to resolve inline references correctly.
|
||||
* @param text the text for the message
|
||||
* @param html whether to apply content type "text/html" for an
|
||||
@@ -798,7 +799,7 @@ public class MimeMessageHelper {
|
||||
/**
|
||||
* Set the given plain text and HTML text as alternatives, offering
|
||||
* both options to the email client. Requires multipart mode.
|
||||
* <p><b>NOTE:</b> Invoke {@link #addInline} <i>after</i> <code>setText</code>;
|
||||
* <p><b>NOTE:</b> Invoke {@link #addInline} <i>after</i> {@code setText};
|
||||
* else, mail readers might not be able to resolve inline references correctly.
|
||||
* @param plainText the plain text for the message
|
||||
* @param htmlText the HTML text for the message
|
||||
@@ -860,16 +861,16 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Add an inline element to the MimeMessage, taking the content from a
|
||||
* <code>javax.activation.DataSource</code>.
|
||||
* {@code javax.activation.DataSource}.
|
||||
* <p>Note that the InputStream returned by the DataSource implementation
|
||||
* needs to be a <i>fresh one on each call</i>, as JavaMail will invoke
|
||||
* <code>getInputStream()</code> multiple times.
|
||||
* <p><b>NOTE:</b> Invoke <code>addInline</code> <i>after</i> {@link #setText};
|
||||
* {@code getInputStream()} multiple times.
|
||||
* <p><b>NOTE:</b> Invoke {@code addInline} <i>after</i> {@link #setText};
|
||||
* else, mail readers might not be able to resolve inline references correctly.
|
||||
* @param contentId the content ID to use. Will end up as "Content-ID" header
|
||||
* in the body part, surrounded by angle brackets: e.g. "myId" -> "<myId>".
|
||||
* Can be referenced in HTML source via src="cid:myId" expressions.
|
||||
* @param dataSource the <code>javax.activation.DataSource</code> to take
|
||||
* @param dataSource the {@code javax.activation.DataSource} to take
|
||||
* the content from, determining the InputStream and the content type
|
||||
* @throws MessagingException in case of errors
|
||||
* @see #addInline(String, java.io.File)
|
||||
@@ -889,11 +890,11 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Add an inline element to the MimeMessage, taking the content from a
|
||||
* <code>java.io.File</code>.
|
||||
* {@code java.io.File}.
|
||||
* <p>The content type will be determined by the name of the given
|
||||
* content file. Do not use this for temporary files with arbitrary
|
||||
* filenames (possibly ending in ".tmp" or the like)!
|
||||
* <p><b>NOTE:</b> Invoke <code>addInline</code> <i>after</i> {@link #setText};
|
||||
* <p><b>NOTE:</b> Invoke {@code addInline} <i>after</i> {@link #setText};
|
||||
* else, mail readers might not be able to resolve inline references correctly.
|
||||
* @param contentId the content ID to use. Will end up as "Content-ID" header
|
||||
* in the body part, surrounded by angle brackets: e.g. "myId" -> "<myId>".
|
||||
@@ -913,14 +914,14 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Add an inline element to the MimeMessage, taking the content from a
|
||||
* <code>org.springframework.core.io.Resource</code>.
|
||||
* {@code org.springframework.core.io.Resource}.
|
||||
* <p>The content type will be determined by the name of the given
|
||||
* content file. Do not use this for temporary files with arbitrary
|
||||
* filenames (possibly ending in ".tmp" or the like)!
|
||||
* <p>Note that the InputStream returned by the Resource implementation
|
||||
* needs to be a <i>fresh one on each call</i>, as JavaMail will invoke
|
||||
* <code>getInputStream()</code> multiple times.
|
||||
* <p><b>NOTE:</b> Invoke <code>addInline</code> <i>after</i> {@link #setText};
|
||||
* {@code getInputStream()} multiple times.
|
||||
* <p><b>NOTE:</b> Invoke {@code addInline} <i>after</i> {@link #setText};
|
||||
* else, mail readers might not be able to resolve inline references correctly.
|
||||
* @param contentId the content ID to use. Will end up as "Content-ID" header
|
||||
* in the body part, surrounded by angle brackets: e.g. "myId" -> "<myId>".
|
||||
@@ -939,14 +940,14 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Add an inline element to the MimeMessage, taking the content from an
|
||||
* <code>org.springframework.core.InputStreamResource</code>, and
|
||||
* {@code org.springframework.core.InputStreamResource}, and
|
||||
* specifying the content type explicitly.
|
||||
* <p>You can determine the content type for any given filename via a Java
|
||||
* Activation Framework's FileTypeMap, for example the one held by this helper.
|
||||
* <p>Note that the InputStream returned by the InputStreamSource implementation
|
||||
* needs to be a <i>fresh one on each call</i>, as JavaMail will invoke
|
||||
* <code>getInputStream()</code> multiple times.
|
||||
* <p><b>NOTE:</b> Invoke <code>addInline</code> <i>after</i> <code>setText</code>;
|
||||
* {@code getInputStream()} multiple times.
|
||||
* <p><b>NOTE:</b> Invoke {@code addInline} <i>after</i> {@code setText};
|
||||
* else, mail readers might not be able to resolve inline references correctly.
|
||||
* @param contentId the content ID to use. Will end up as "Content-ID" header
|
||||
* in the body part, surrounded by angle brackets: e.g. "myId" -> "<myId>".
|
||||
@@ -960,7 +961,7 @@ public class MimeMessageHelper {
|
||||
* @see #addInline(String, javax.activation.DataSource)
|
||||
*/
|
||||
public void addInline(String contentId, InputStreamSource inputStreamSource, String contentType)
|
||||
throws MessagingException {
|
||||
throws MessagingException {
|
||||
|
||||
Assert.notNull(inputStreamSource, "InputStreamSource must not be null");
|
||||
if (inputStreamSource instanceof Resource && ((Resource) inputStreamSource).isOpen()) {
|
||||
@@ -974,13 +975,13 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Add an attachment to the MimeMessage, taking the content from a
|
||||
* <code>javax.activation.DataSource</code>.
|
||||
* {@code javax.activation.DataSource}.
|
||||
* <p>Note that the InputStream returned by the DataSource implementation
|
||||
* needs to be a <i>fresh one on each call</i>, as JavaMail will invoke
|
||||
* <code>getInputStream()</code> multiple times.
|
||||
* {@code getInputStream()} multiple times.
|
||||
* @param attachmentFilename the name of the attachment as it will
|
||||
* appear in the mail (the content type will be determined by this)
|
||||
* @param dataSource the <code>javax.activation.DataSource</code> to take
|
||||
* @param dataSource the {@code javax.activation.DataSource} to take
|
||||
* the content from, determining the InputStream and the content type
|
||||
* @throws MessagingException in case of errors
|
||||
* @see #addAttachment(String, org.springframework.core.io.InputStreamSource)
|
||||
@@ -989,16 +990,21 @@ public class MimeMessageHelper {
|
||||
public void addAttachment(String attachmentFilename, DataSource dataSource) throws MessagingException {
|
||||
Assert.notNull(attachmentFilename, "Attachment filename must not be null");
|
||||
Assert.notNull(dataSource, "DataSource must not be null");
|
||||
MimeBodyPart mimeBodyPart = new MimeBodyPart();
|
||||
mimeBodyPart.setDisposition(MimeBodyPart.ATTACHMENT);
|
||||
mimeBodyPart.setFileName(attachmentFilename);
|
||||
mimeBodyPart.setDataHandler(new DataHandler(dataSource));
|
||||
getRootMimeMultipart().addBodyPart(mimeBodyPart);
|
||||
try {
|
||||
MimeBodyPart mimeBodyPart = new MimeBodyPart();
|
||||
mimeBodyPart.setDisposition(MimeBodyPart.ATTACHMENT);
|
||||
mimeBodyPart.setFileName(MimeUtility.encodeText(attachmentFilename));
|
||||
mimeBodyPart.setDataHandler(new DataHandler(dataSource));
|
||||
getRootMimeMultipart().addBodyPart(mimeBodyPart);
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
throw new MessagingException("Failed to encode attachment filename", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an attachment to the MimeMessage, taking the content from a
|
||||
* <code>java.io.File</code>.
|
||||
* {@code java.io.File}.
|
||||
* <p>The content type will be determined by the name of the given
|
||||
* content file. Do not use this for temporary files with arbitrary
|
||||
* filenames (possibly ending in ".tmp" or the like)!
|
||||
@@ -1018,13 +1024,13 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Add an attachment to the MimeMessage, taking the content from an
|
||||
* <code>org.springframework.core.io.InputStreamResource</code>.
|
||||
* {@code org.springframework.core.io.InputStreamResource}.
|
||||
* <p>The content type will be determined by the given filename for
|
||||
* the attachment. Thus, any content source will be fine, including
|
||||
* temporary files with arbitrary filenames.
|
||||
* <p>Note that the InputStream returned by the InputStreamSource
|
||||
* implementation needs to be a <i>fresh one on each call</i>, as
|
||||
* JavaMail will invoke <code>getInputStream()</code> multiple times.
|
||||
* JavaMail will invoke {@code getInputStream()} multiple times.
|
||||
* @param attachmentFilename the name of the attachment as it will
|
||||
* appear in the mail
|
||||
* @param inputStreamSource the resource to take the content from
|
||||
@@ -1035,7 +1041,7 @@ public class MimeMessageHelper {
|
||||
* @see org.springframework.core.io.Resource
|
||||
*/
|
||||
public void addAttachment(String attachmentFilename, InputStreamSource inputStreamSource)
|
||||
throws MessagingException {
|
||||
throws MessagingException {
|
||||
|
||||
String contentType = getFileTypeMap().getContentType(attachmentFilename);
|
||||
addAttachment(attachmentFilename, inputStreamSource, contentType);
|
||||
@@ -1043,10 +1049,10 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Add an attachment to the MimeMessage, taking the content from an
|
||||
* <code>org.springframework.core.io.InputStreamResource</code>.
|
||||
* {@code org.springframework.core.io.InputStreamResource}.
|
||||
* <p>Note that the InputStream returned by the InputStreamSource
|
||||
* implementation needs to be a <i>fresh one on each call</i>, as
|
||||
* JavaMail will invoke <code>getInputStream()</code> multiple times.
|
||||
* JavaMail will invoke {@code getInputStream()} multiple times.
|
||||
* @param attachmentFilename the name of the attachment as it will
|
||||
* appear in the mail
|
||||
* @param inputStreamSource the resource to take the content from
|
||||
@@ -1059,7 +1065,7 @@ public class MimeMessageHelper {
|
||||
*/
|
||||
public void addAttachment(
|
||||
String attachmentFilename, InputStreamSource inputStreamSource, String contentType)
|
||||
throws MessagingException {
|
||||
throws MessagingException {
|
||||
|
||||
Assert.notNull(inputStreamSource, "InputStreamSource must not be null");
|
||||
if (inputStreamSource instanceof Resource && ((Resource) inputStreamSource).isOpen()) {
|
||||
@@ -1079,7 +1085,7 @@ public class MimeMessageHelper {
|
||||
* @return the Activation Framework DataSource
|
||||
*/
|
||||
protected DataSource createDataSource(
|
||||
final InputStreamSource inputStreamSource, final String contentType, final String name) {
|
||||
final InputStreamSource inputStreamSource, final String contentType, final String name) {
|
||||
|
||||
return new DataSource() {
|
||||
public InputStream getInputStream() throws IOException {
|
||||
|
||||
+6
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -43,10 +43,15 @@ public class TimerManagerTaskScheduler extends TimerManagerAccessor implements T
|
||||
|
||||
private volatile ErrorHandler errorHandler;
|
||||
|
||||
|
||||
/**
|
||||
* Provide an {@link ErrorHandler} strategy.
|
||||
*/
|
||||
public void setErrorHandler(ErrorHandler errorHandler) {
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
|
||||
public ScheduledFuture schedule(Runnable task, Trigger trigger) {
|
||||
return new ReschedulingTimerListener(errorHandlingTask(task, true), trigger).schedule();
|
||||
}
|
||||
|
||||
+1
-21
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -65,26 +65,6 @@ import org.springframework.util.Assert;
|
||||
* {@link org.springframework.jca.work.glassfish.GlassFishWorkManagerTaskExecutor}
|
||||
* classes which are the direct equivalent of this CommonJ adapter class.
|
||||
*
|
||||
* <p>A similar facility is available on WebSphere 5.1, under the name
|
||||
* "Asynch Beans". Its central interface is called WorkManager too and is
|
||||
* also obtained from JNDI, just like a standard CommonJ WorkManager.
|
||||
* However, this WorkManager variant is notably different: The central
|
||||
* execution method is called "startWork" instead of "schedule",
|
||||
* and takes a slightly different Work interface as parameter.
|
||||
*
|
||||
* <p>Support for this WebSphere 5.1 variant can be built with this class
|
||||
* and its helper DelegatingWork as template: Call the WorkManager's
|
||||
* <code>startWork(Work)</code> instead of <code>schedule(Work)</code>
|
||||
* in the <code>execute(Runnable)</code> implementation. Furthermore,
|
||||
* for simplicity's sake, drop the entire "Implementation of the CommonJ
|
||||
* WorkManager interface" section (and the corresponding
|
||||
* <code>implements WorkManager</code> clause at the class level).
|
||||
* Of course, you also need to change all <code>commonj.work</code> imports in
|
||||
* your WorkManagerTaskExecutor and DelegatingWork variants to the corresponding
|
||||
* WebSphere API imports (<code>com.ibm.websphere.asynchbeans.WorkManager</code>
|
||||
* and <code>com.ibm.websphere.asynchbeans.Work</code>, respectively).
|
||||
* This should be sufficient to get a TaskExecutor adapter for WebSphere 5.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -95,8 +95,8 @@ public class SchedulerAccessorBean extends SchedulerAccessor implements BeanFact
|
||||
if (this.beanFactory instanceof ListableBeanFactory) {
|
||||
ListableBeanFactory lbf = (ListableBeanFactory) this.beanFactory;
|
||||
String[] beanNames = lbf.getBeanNamesForType(Scheduler.class);
|
||||
for (int i = 0; i < beanNames.length; i++) {
|
||||
Scheduler schedulerBean = (Scheduler) lbf.getBean(beanNames[i]);
|
||||
for (String beanName : beanNames) {
|
||||
Scheduler schedulerBean = (Scheduler) lbf.getBean(beanName);
|
||||
if (schedulerName.equals(schedulerBean.getSchedulerName())) {
|
||||
return schedulerBean;
|
||||
}
|
||||
|
||||
+27
-24
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
@@ -47,12 +48,12 @@ import org.springframework.util.CollectionUtils;
|
||||
* <p>The optional "configLocation" property sets the location of a FreeMarker
|
||||
* properties file, within the current application. FreeMarker properties can be
|
||||
* overridden via "freemarkerSettings". All of these properties will be set by
|
||||
* calling FreeMarker's <code>Configuration.setSettings()</code> method and are
|
||||
* calling FreeMarker's {@code Configuration.setSettings()} method and are
|
||||
* subject to constraints set by FreeMarker.
|
||||
*
|
||||
* <p>The "freemarkerVariables" property can be used to specify a Map of
|
||||
* shared variables that will be applied to the Configuration via the
|
||||
* <code>setAllSharedVariables()</code> method. Like <code>setSettings()</code>,
|
||||
* {@code setAllSharedVariables()} method. Like {@code setSettings()},
|
||||
* these entries are subject to FreeMarker constraints.
|
||||
*
|
||||
* <p>The simplest way to use this class is to specify a "templateLoaderPath";
|
||||
@@ -109,7 +110,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
|
||||
/**
|
||||
* Set properties that contain well-known FreeMarker keys which will be
|
||||
* passed to FreeMarker's <code>Configuration.setSettings</code> method.
|
||||
* passed to FreeMarker's {@code Configuration.setSettings} method.
|
||||
* @see freemarker.template.Configuration#setSettings
|
||||
*/
|
||||
public void setFreemarkerSettings(Properties settings) {
|
||||
@@ -118,7 +119,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
|
||||
/**
|
||||
* Set a Map that contains well-known FreeMarker objects which will be passed
|
||||
* to FreeMarker's <code>Configuration.setAllSharedVariables()</code> method.
|
||||
* to FreeMarker's {@code Configuration.setAllSharedVariables()} method.
|
||||
* @see freemarker.template.Configuration#setAllSharedVariables
|
||||
*/
|
||||
public void setFreemarkerVariables(Map<String, Object> variables) {
|
||||
@@ -138,7 +139,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a List of <code>TemplateLoader<code>s that will be used to search
|
||||
* Set a List of {@code TemplateLoader}s that will be used to search
|
||||
* for templates. For example, one or more custom loaders such as database
|
||||
* loaders could be configured and injected here.
|
||||
* @deprecated as of Spring 2.0.1, in favor of the "preTemplateLoaders"
|
||||
@@ -147,14 +148,14 @@ public class FreeMarkerConfigurationFactory {
|
||||
* @see #setPostTemplateLoaders
|
||||
*/
|
||||
@Deprecated
|
||||
public void setTemplateLoaders(TemplateLoader[] templateLoaders) {
|
||||
public void setTemplateLoaders(TemplateLoader... templateLoaders) {
|
||||
if (templateLoaders != null) {
|
||||
this.templateLoaders.addAll(Arrays.asList(templateLoaders));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a List of <code>TemplateLoader<code>s that will be used to search
|
||||
* Set a List of {@code TemplateLoader}s that will be used to search
|
||||
* for templates. For example, one or more custom loaders such as database
|
||||
* loaders could be configured and injected here.
|
||||
* <p>The {@link TemplateLoader TemplateLoaders} specified here will be
|
||||
@@ -164,12 +165,12 @@ public class FreeMarkerConfigurationFactory {
|
||||
* @see #setTemplateLoaderPaths
|
||||
* @see #postProcessTemplateLoaders
|
||||
*/
|
||||
public void setPreTemplateLoaders(TemplateLoader[] preTemplateLoaders) {
|
||||
public void setPreTemplateLoaders(TemplateLoader... preTemplateLoaders) {
|
||||
this.preTemplateLoaders = Arrays.asList(preTemplateLoaders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a List of <code>TemplateLoader<code>s that will be used to search
|
||||
* Set a List of {@code TemplateLoader}s that will be used to search
|
||||
* for templates. For example, one or more custom loaders such as database
|
||||
* loaders can be configured.
|
||||
* <p>The {@link TemplateLoader TemplateLoaders} specified here will be
|
||||
@@ -179,7 +180,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
* @see #setTemplateLoaderPaths
|
||||
* @see #postProcessTemplateLoaders
|
||||
*/
|
||||
public void setPostTemplateLoaders(TemplateLoader[] postTemplateLoaders) {
|
||||
public void setPostTemplateLoaders(TemplateLoader... postTemplateLoaders) {
|
||||
this.postTemplateLoaders = Arrays.asList(postTemplateLoaders);
|
||||
}
|
||||
|
||||
@@ -198,20 +199,20 @@ public class FreeMarkerConfigurationFactory {
|
||||
* pseudo URLs are supported, as understood by ResourceEditor. Allows for
|
||||
* relative paths when running in an ApplicationContext.
|
||||
* <p>Will define a path for the default FreeMarker template loader.
|
||||
* If a specified resource cannot be resolved to a <code>java.io.File</code>,
|
||||
* If a specified resource cannot be resolved to a {@code java.io.File},
|
||||
* a generic SpringTemplateLoader will be used, without modification detection.
|
||||
* <p>To enforce the use of SpringTemplateLoader, i.e. to not resolve a path
|
||||
* as file system resource in any case, turn off the "preferFileSystemAccess"
|
||||
* flag. See the latter's javadoc for details.
|
||||
* <p>If you wish to specify your own list of TemplateLoaders, do not set this
|
||||
* property and instead use <code>setTemplateLoaders(List templateLoaders)</code>
|
||||
* property and instead use {@code setTemplateLoaders(List templateLoaders)}
|
||||
* @see org.springframework.core.io.ResourceEditor
|
||||
* @see org.springframework.context.ApplicationContext#getResource
|
||||
* @see freemarker.template.Configuration#setDirectoryForTemplateLoading
|
||||
* @see SpringTemplateLoader
|
||||
* @see #setTemplateLoaders
|
||||
*/
|
||||
public void setTemplateLoaderPaths(String[] templateLoaderPaths) {
|
||||
public void setTemplateLoaderPaths(String... templateLoaderPaths) {
|
||||
this.templateLoaderPaths = templateLoaderPaths;
|
||||
}
|
||||
|
||||
@@ -229,7 +230,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
* Return the Spring ResourceLoader to use for loading FreeMarker template files.
|
||||
*/
|
||||
protected ResourceLoader getResourceLoader() {
|
||||
return resourceLoader;
|
||||
return this.resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,7 +253,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
* Return whether to prefer file system access for template loading.
|
||||
*/
|
||||
protected boolean isPreferFileSystemAccess() {
|
||||
return preferFileSystemAccess;
|
||||
return this.preferFileSystemAccess;
|
||||
}
|
||||
|
||||
|
||||
@@ -293,25 +294,27 @@ public class FreeMarkerConfigurationFactory {
|
||||
config.setDefaultEncoding(this.defaultEncoding);
|
||||
}
|
||||
|
||||
List<TemplateLoader> templateLoaders = new LinkedList<TemplateLoader>(this.templateLoaders);
|
||||
|
||||
// Register template loaders that are supposed to kick in early.
|
||||
if (this.preTemplateLoaders != null) {
|
||||
this.templateLoaders.addAll(this.preTemplateLoaders);
|
||||
templateLoaders.addAll(this.preTemplateLoaders);
|
||||
}
|
||||
|
||||
// Register default template loaders.
|
||||
if (this.templateLoaderPaths != null) {
|
||||
for (String path : this.templateLoaderPaths) {
|
||||
this.templateLoaders.add(getTemplateLoaderForPath(path));
|
||||
templateLoaders.add(getTemplateLoaderForPath(path));
|
||||
}
|
||||
}
|
||||
postProcessTemplateLoaders(this.templateLoaders);
|
||||
postProcessTemplateLoaders(templateLoaders);
|
||||
|
||||
// Register template loaders that are supposed to kick in late.
|
||||
if (this.postTemplateLoaders != null) {
|
||||
this.templateLoaders.addAll(this.postTemplateLoaders);
|
||||
templateLoaders.addAll(this.postTemplateLoaders);
|
||||
}
|
||||
|
||||
TemplateLoader loader = getAggregateTemplateLoader(this.templateLoaders);
|
||||
TemplateLoader loader = getAggregateTemplateLoader(templateLoaders);
|
||||
if (loader != null) {
|
||||
config.setTemplateLoader(loader);
|
||||
}
|
||||
@@ -323,7 +326,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
/**
|
||||
* Return a new Configuration object. Subclasses can override this for
|
||||
* custom initialization, or for using a mock object for testing.
|
||||
* <p>Called by <code>createConfiguration()</code>.
|
||||
* <p>Called by {@code createConfiguration()}.
|
||||
* @return the Configuration object
|
||||
* @throws IOException if a config file wasn't found
|
||||
* @throws TemplateException on FreeMarker initialization failure
|
||||
@@ -374,7 +377,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
* To be overridden by subclasses that want to to register custom
|
||||
* TemplateLoader instances after this factory created its default
|
||||
* template loaders.
|
||||
* <p>Called by <code>createConfiguration()</code>. Note that specified
|
||||
* <p>Called by {@code createConfiguration()}. Note that specified
|
||||
* "postTemplateLoaders" will be registered <i>after</i> any loaders
|
||||
* registered by this callback; as a consequence, they are are <i>not</i>
|
||||
* included in the given List.
|
||||
@@ -411,7 +414,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
* To be overridden by subclasses that want to to perform custom
|
||||
* post-processing of the Configuration object after this factory
|
||||
* performed its default initialization.
|
||||
* <p>Called by <code>createConfiguration()</code>.
|
||||
* <p>Called by {@code createConfiguration()}.
|
||||
* @param config the current Configuration object
|
||||
* @throws IOException if a config file wasn't found
|
||||
* @throws TemplateException on FreeMarker initialization failure
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+42
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,6 +39,7 @@ import org.springframework.util.Assert;
|
||||
* {@code CacheOperationSource}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@@ -71,6 +72,16 @@ public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperati
|
||||
this.annotationParsers.add(new SpringCacheAnnotationParser());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a custom AnnotationCacheOperationSource.
|
||||
* @param annotationParser the CacheAnnotationParser to use
|
||||
*/
|
||||
public AnnotationCacheOperationSource(CacheAnnotationParser annotationParser) {
|
||||
this.publicMethodsOnly = true;
|
||||
Assert.notNull(annotationParser, "CacheAnnotationParser must not be null");
|
||||
this.annotationParsers = Collections.singleton(annotationParser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a custom AnnotationCacheOperationSource.
|
||||
* @param annotationParsers the CacheAnnotationParser to use
|
||||
@@ -83,6 +94,16 @@ public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperati
|
||||
this.annotationParsers = parsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a custom AnnotationCacheOperationSource.
|
||||
* @param annotationParsers the CacheAnnotationParser to use
|
||||
*/
|
||||
public AnnotationCacheOperationSource(Set<CacheAnnotationParser> annotationParsers) {
|
||||
this.publicMethodsOnly = true;
|
||||
Assert.notEmpty(annotationParsers, "At least one CacheAnnotationParser needs to be specified");
|
||||
this.annotationParsers = annotationParsers;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Collection<CacheOperation> findCacheOperations(Class<?> clazz) {
|
||||
@@ -106,7 +127,6 @@ public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperati
|
||||
*/
|
||||
protected Collection<CacheOperation> determineCacheOperations(AnnotatedElement ae) {
|
||||
Collection<CacheOperation> ops = null;
|
||||
|
||||
for (CacheAnnotationParser annotationParser : this.annotationParsers) {
|
||||
Collection<CacheOperation> annOps = annotationParser.parseCacheAnnotations(ae);
|
||||
if (annOps != null) {
|
||||
@@ -126,4 +146,24 @@ public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperati
|
||||
protected boolean allowPublicMethodsOnly() {
|
||||
return this.publicMethodsOnly;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof AnnotationCacheOperationSource)) {
|
||||
return false;
|
||||
}
|
||||
AnnotationCacheOperationSource otherCos = (AnnotationCacheOperationSource) other;
|
||||
return (this.annotationParsers.equals(otherCos.annotationParsers) &&
|
||||
this.publicMethodsOnly == otherCos.publicMethodsOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.annotationParsers.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+14
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -135,7 +135,7 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
||||
return ops;
|
||||
}
|
||||
|
||||
private static <T extends Annotation> Collection<T> getAnnotations(AnnotatedElement ae, Class<T> annotationType) {
|
||||
private <T extends Annotation> Collection<T> getAnnotations(AnnotatedElement ae, Class<T> annotationType) {
|
||||
Collection<T> anns = new ArrayList<T>(2);
|
||||
|
||||
// look at raw annotation
|
||||
@@ -154,4 +154,15 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
||||
|
||||
return (anns.isEmpty() ? null : anns);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return (this == other || other instanceof SpringCacheAnnotationParser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return SpringCacheAnnotationParser.class.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -47,8 +47,7 @@ abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut
|
||||
return false;
|
||||
}
|
||||
CacheOperationSourcePointcut otherPc = (CacheOperationSourcePointcut) other;
|
||||
return ObjectUtils.nullSafeEquals(getCacheOperationSource(),
|
||||
otherPc.getCacheOperationSource());
|
||||
return ObjectUtils.nullSafeEquals(getCacheOperationSource(), otherPc.getCacheOperationSource());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,16 +39,16 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
*/
|
||||
class ExpressionEvaluator {
|
||||
|
||||
private SpelExpressionParser parser = new SpelExpressionParser();
|
||||
private final SpelExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
// shared param discoverer since it caches data internally
|
||||
private ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||
private final ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
private Map<String, Expression> conditionCache = new ConcurrentHashMap<String, Expression>();
|
||||
private final Map<String, Expression> conditionCache = new ConcurrentHashMap<String, Expression>();
|
||||
|
||||
private Map<String, Expression> keyCache = new ConcurrentHashMap<String, Expression>();
|
||||
private final Map<String, Expression> keyCache = new ConcurrentHashMap<String, Expression>();
|
||||
|
||||
private Map<String, Method> targetMethodCache = new ConcurrentHashMap<String, Method>();
|
||||
private final Map<String, Method> targetMethodCache = new ConcurrentHashMap<String, Method>();
|
||||
|
||||
|
||||
public EvaluationContext createEvaluationContext(
|
||||
|
||||
+5
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,12 +26,10 @@ import java.util.concurrent.ConcurrentMap;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract base class implementing the common {@link CacheManager}
|
||||
* methods. Useful for 'static' environments where the backing caches do
|
||||
* not change.
|
||||
* Abstract base class implementing the common {@link CacheManager} methods.
|
||||
* Useful for 'static' environments where the backing caches do not change.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
@@ -46,10 +44,10 @@ public abstract class AbstractCacheManager implements CacheManager, Initializing
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
Collection<? extends Cache> caches = loadCaches();
|
||||
Assert.notEmpty(caches, "loadCaches must not return an empty Collection");
|
||||
this.cacheMap.clear();
|
||||
|
||||
// preserve the initial order of the cache names
|
||||
this.cacheMap.clear();
|
||||
this.cacheNames.clear();
|
||||
for (Cache cache : caches) {
|
||||
this.cacheMap.put(cache.getName(), cache);
|
||||
this.cacheNames.add(cache.getName());
|
||||
|
||||
Vendored
+31
-29
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -40,7 +40,35 @@ import org.springframework.cache.CacheManager;
|
||||
public class NoOpCacheManager implements CacheManager {
|
||||
|
||||
private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
|
||||
private Set<String> names = new LinkedHashSet<String>();
|
||||
|
||||
private final Set<String> cacheNames = new LinkedHashSet<String>();
|
||||
|
||||
|
||||
/**
|
||||
* This implementation always returns a {@link Cache} implementation that will not store items.
|
||||
* Additionally, the request cache will be remembered by the manager for consistency.
|
||||
*/
|
||||
public Cache getCache(String name) {
|
||||
Cache cache = this.caches.get(name);
|
||||
if (cache == null) {
|
||||
this.caches.putIfAbsent(name, new NoOpCache(name));
|
||||
synchronized (this.cacheNames) {
|
||||
this.cacheNames.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
return this.caches.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation returns the name of the caches previously requested.
|
||||
*/
|
||||
public Collection<String> getCacheNames() {
|
||||
synchronized (this.cacheNames) {
|
||||
return Collections.unmodifiableSet(this.cacheNames);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class NoOpCache implements Cache {
|
||||
|
||||
@@ -61,7 +89,7 @@ public class NoOpCacheManager implements CacheManager {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public Object getNativeCache() {
|
||||
@@ -72,30 +100,4 @@ public class NoOpCacheManager implements CacheManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* This implementation always returns a {@link Cache} implementation that will not
|
||||
* store items. Additionally, the request cache will be remembered by the manager for consistency.
|
||||
*/
|
||||
public Cache getCache(String name) {
|
||||
Cache cache = caches.get(name);
|
||||
if (cache == null) {
|
||||
caches.putIfAbsent(name, new NoOpCache(name));
|
||||
synchronized (names) {
|
||||
names.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
return caches.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* This implementation returns the name of the caches previously requested.
|
||||
*/
|
||||
public Collection<String> getCacheNames() {
|
||||
return Collections.unmodifiableSet(names);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,12 +39,13 @@ import static org.springframework.context.annotation.MetadataUtils.*;
|
||||
*/
|
||||
public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
|
||||
|
||||
protected Class<? extends Annotation> scopeAnnotationType = Scope.class;
|
||||
|
||||
private final ScopedProxyMode defaultProxyMode;
|
||||
|
||||
protected Class<? extends Annotation> scopeAnnotationType = Scope.class;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance of the <code>AnnotationScopeMetadataResolver</code> class.
|
||||
* Create a new instance of the {@code AnnotationScopeMetadataResolver} class.
|
||||
* @see #AnnotationScopeMetadataResolver(ScopedProxyMode)
|
||||
* @see ScopedProxyMode#NO
|
||||
*/
|
||||
@@ -53,7 +54,7 @@ public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the <code>AnnotationScopeMetadataResolver</code> class.
|
||||
* Create a new instance of the {@code AnnotationScopeMetadataResolver} class.
|
||||
* @param defaultProxyMode the desired scoped-proxy mode
|
||||
*/
|
||||
public AnnotationScopeMetadataResolver(ScopedProxyMode defaultProxyMode) {
|
||||
@@ -77,8 +78,7 @@ public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
|
||||
ScopeMetadata metadata = new ScopeMetadata();
|
||||
if (definition instanceof AnnotatedBeanDefinition) {
|
||||
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
|
||||
AnnotationAttributes attributes =
|
||||
attributesFor(annDef.getMetadata(), this.scopeAnnotationType);
|
||||
AnnotationAttributes attributes = attributesFor(annDef.getMetadata(), this.scopeAnnotationType);
|
||||
if (attributes != null) {
|
||||
metadata.setScopeName(attributes.getString("value"));
|
||||
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
|
||||
|
||||
+8
-7
@@ -25,6 +25,7 @@ import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
@@ -120,6 +121,13 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ResourceLoader that this component provider uses.
|
||||
*/
|
||||
public final ResourceLoader getResourceLoader() {
|
||||
return this.resourcePatternResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Environment to use when resolving placeholders and evaluating
|
||||
* {@link Profile @Profile}-annotated component classes.
|
||||
@@ -134,13 +142,6 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
return this.environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ResourceLoader that this component provider uses.
|
||||
*/
|
||||
public final ResourceLoader getResourceLoader() {
|
||||
return this.resourcePatternResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource pattern to use when scanning the classpath.
|
||||
* This value will be appended to each base package name.
|
||||
|
||||
+1
-5
@@ -513,10 +513,6 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
|
||||
protected boolean shareable = true;
|
||||
|
||||
private volatile boolean cached = false;
|
||||
|
||||
private volatile Object cachedFieldValue;
|
||||
|
||||
public ResourceElement(Member member, PropertyDescriptor pd) {
|
||||
super(member, pd);
|
||||
}
|
||||
@@ -729,4 +725,4 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -54,8 +54,7 @@ class ComponentScanAnnotationParser {
|
||||
private final BeanNameGenerator beanNameGenerator;
|
||||
|
||||
|
||||
public ComponentScanAnnotationParser(
|
||||
ResourceLoader resourceLoader, Environment environment,
|
||||
public ComponentScanAnnotationParser(ResourceLoader resourceLoader, Environment environment,
|
||||
BeanNameGenerator beanNameGenerator, BeanDefinitionRegistry registry) {
|
||||
|
||||
this.resourceLoader = resourceLoader;
|
||||
@@ -67,7 +66,7 @@ class ComponentScanAnnotationParser {
|
||||
|
||||
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan) {
|
||||
ClassPathBeanDefinitionScanner scanner =
|
||||
new ClassPathBeanDefinitionScanner(registry, componentScan.getBoolean("useDefaultFilters"));
|
||||
new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"));
|
||||
|
||||
Assert.notNull(this.environment, "Environment must not be null");
|
||||
scanner.setEnvironment(this.environment);
|
||||
@@ -77,14 +76,14 @@ class ComponentScanAnnotationParser {
|
||||
|
||||
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
|
||||
boolean useInheritedGenerator = BeanNameGenerator.class.equals(generatorClass);
|
||||
scanner.setBeanNameGenerator(useInheritedGenerator
|
||||
? this.beanNameGenerator
|
||||
: BeanUtils.instantiateClass(generatorClass));
|
||||
scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
|
||||
BeanUtils.instantiateClass(generatorClass));
|
||||
|
||||
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
|
||||
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
|
||||
scanner.setScopedProxyMode(scopedProxyMode);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
|
||||
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
|
||||
}
|
||||
@@ -121,7 +120,7 @@ class ComponentScanAnnotationParser {
|
||||
throw new IllegalStateException("At least one base package must be specified");
|
||||
}
|
||||
|
||||
return scanner.doScan(basePackages.toArray(new String[]{}));
|
||||
return scanner.doScan(StringUtils.toStringArray(basePackages));
|
||||
}
|
||||
|
||||
private List<TypeFilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
|
||||
@@ -153,4 +152,5 @@ class ComponentScanAnnotationParser {
|
||||
}
|
||||
return typeFilters;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-4
@@ -56,7 +56,6 @@ class ConfigurationClassEnhancer {
|
||||
DisposableBeanMethodInterceptor.class, NoOp.class };
|
||||
|
||||
private static final CallbackFilter CALLBACK_FILTER = new CallbackFilter() {
|
||||
|
||||
public int accept(Method candidateMethod) {
|
||||
// Set up the callback filter to return the index of the BeanMethodInterceptor when
|
||||
// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
|
||||
@@ -72,7 +71,6 @@ class ConfigurationClassEnhancer {
|
||||
|
||||
private static final Callback DISPOSABLE_BEAN_METHOD_INTERCEPTOR = new DisposableBeanMethodInterceptor();
|
||||
|
||||
|
||||
private final Callback[] callbackInstances;
|
||||
|
||||
|
||||
@@ -162,6 +160,7 @@ class ConfigurationClassEnhancer {
|
||||
private static class GetObjectMethodInterceptor implements MethodInterceptor {
|
||||
|
||||
private final ConfigurableBeanFactory beanFactory;
|
||||
|
||||
private final String beanName;
|
||||
|
||||
public GetObjectMethodInterceptor(ConfigurableBeanFactory beanFactory, String beanName) {
|
||||
@@ -296,7 +295,8 @@ class ConfigurationClassEnhancer {
|
||||
this.beanFactory.setCurrentlyInCreation(beanName, false);
|
||||
}
|
||||
return this.beanFactory.getBean(beanName);
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
if (alreadyInCreation) {
|
||||
this.beanFactory.setCurrentlyInCreation(beanName, true);
|
||||
}
|
||||
@@ -347,6 +347,6 @@ class ConfigurationClassEnhancer {
|
||||
Enhancer.registerCallbacks(fbSubclass, callbackInstances);
|
||||
return fbSubclass.newInstance();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+47
-47
@@ -16,15 +16,16 @@
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import static org.springframework.context.annotation.MetadataUtils.attributesFor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
@@ -51,8 +52,6 @@ import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.context.annotation.MetadataUtils.*;
|
||||
|
||||
/**
|
||||
* Parses a {@link Configuration} class definition, populating a collection of
|
||||
* {@link ConfigurationClass} objects (parsing a single Configuration class may result in
|
||||
@@ -221,10 +220,9 @@ class ConfigurationClassParser {
|
||||
}
|
||||
|
||||
// process any @Import annotations
|
||||
List<AnnotationAttributes> imports =
|
||||
findAllAnnotationAttributes(Import.class, metadata.getClassName(), true);
|
||||
for (AnnotationAttributes importAnno : imports) {
|
||||
processImport(configClass, importAnno.getStringArray("value"), true);
|
||||
Set<String> imports = getImports(metadata.getClassName(), null, new HashSet<String>());
|
||||
if (imports != null && !imports.isEmpty()) {
|
||||
processImport(configClass, imports.toArray(new String[imports.size()]), true);
|
||||
}
|
||||
|
||||
// process any @ImportResource annotations
|
||||
@@ -252,6 +250,16 @@ class ConfigurationClassParser {
|
||||
Class<?> clazz = ((StandardAnnotationMetadata) metadata).getIntrospectedClass();
|
||||
return new StandardAnnotationMetadata(clazz.getSuperclass(), true);
|
||||
}
|
||||
else if (superclass.startsWith("java")) {
|
||||
// never load core JDK classes via ASM, in particular not java.lang.Object!
|
||||
try {
|
||||
return new StandardAnnotationMetadata(
|
||||
this.resourceLoader.getClassLoader().loadClass(superclass), true);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(superclass);
|
||||
return reader.getAnnotationMetadata();
|
||||
@@ -264,45 +272,36 @@ class ConfigurationClassParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of attribute maps for all declarations of the given annotation
|
||||
* on the given annotated class using the given MetadataReaderFactory to introspect
|
||||
* annotation metadata. Meta-annotations are ordered first in the list, and if the
|
||||
* target annotation is declared directly on the class, its map of attributes will be
|
||||
* ordered last in the list.
|
||||
* @param targetAnnotation the annotation to search for, both locally and as a meta-annotation
|
||||
* @param annotatedClassName the class to inspect
|
||||
* @param classValuesAsString whether class attributes should be returned as strings
|
||||
* Recursively collect all declared {@code @Import} values. Unlike most
|
||||
* meta-annotations it is valid to have several {@code @Import}s declared with
|
||||
* different values, the usual process or returning values from the first
|
||||
* meta-annotation on a class is not sufficient.
|
||||
* <p>For example, it is common for a {@code @Configuration} class to declare direct
|
||||
* {@code @Import}s in addition to meta-imports originating from an {@code @Enable}
|
||||
* annotation.
|
||||
* @param className the class name to search
|
||||
* @param imports the imports collected so far or {@code null}
|
||||
* @param visited used to track visited classes to prevent infinite recursion (must not be null)
|
||||
* @return a set of all {@link Import#value() import values} or {@code null}
|
||||
* @throws IOException if there is any problem reading metadata from the named class
|
||||
*/
|
||||
private List<AnnotationAttributes> findAllAnnotationAttributes(
|
||||
Class<? extends Annotation> targetAnnotation, String annotatedClassName,
|
||||
boolean classValuesAsString) throws IOException {
|
||||
|
||||
List<AnnotationAttributes> allAttribs = new ArrayList<AnnotationAttributes>();
|
||||
|
||||
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(annotatedClassName);
|
||||
AnnotationMetadata metadata = reader.getAnnotationMetadata();
|
||||
String targetAnnotationType = targetAnnotation.getName();
|
||||
|
||||
for (String annotationType : metadata.getAnnotationTypes()) {
|
||||
if (annotationType.equals(targetAnnotationType)) {
|
||||
continue;
|
||||
private Set<String> getImports(String className, Set<String> imports,
|
||||
Set<String> visited) throws IOException {
|
||||
if (visited.add(className)) {
|
||||
AnnotationMetadata metadata = metadataReaderFactory.getMetadataReader(className).getAnnotationMetadata();
|
||||
for (String annotationType : metadata.getAnnotationTypes()) {
|
||||
imports = getImports(annotationType, imports, visited);
|
||||
}
|
||||
AnnotationMetadata metaAnnotations =
|
||||
this.metadataReaderFactory.getMetadataReader(annotationType).getAnnotationMetadata();
|
||||
AnnotationAttributes targetAttribs =
|
||||
AnnotationAttributes.fromMap(metaAnnotations.getAnnotationAttributes(targetAnnotationType, classValuesAsString));
|
||||
if (targetAttribs != null) {
|
||||
allAttribs.add(targetAttribs);
|
||||
Map<String, Object> attributes = metadata.getAnnotationAttributes(Import.class.getName(), true);
|
||||
if (attributes != null) {
|
||||
String[] value = (String[]) attributes.get("value");
|
||||
if (value != null && value.length > 0) {
|
||||
imports = (imports == null ? new LinkedHashSet<String>() : imports);
|
||||
imports.addAll(Arrays.asList(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AnnotationAttributes localAttribs =
|
||||
AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(targetAnnotationType, classValuesAsString));
|
||||
if (localAttribs != null) {
|
||||
allAttribs.add(localAttribs);
|
||||
}
|
||||
|
||||
return allAttribs;
|
||||
return imports;
|
||||
}
|
||||
|
||||
private void processImport(ConfigurationClass configClass, String[] classesToImport, boolean checkForCircularImports) throws IOException {
|
||||
@@ -317,7 +316,8 @@ class ConfigurationClassParser {
|
||||
if (new AssignableTypeFilter(ImportSelector.class).match(reader, this.metadataReaderFactory)) {
|
||||
// the candidate class is an ImportSelector -> delegate to it to determine imports
|
||||
try {
|
||||
ImportSelector selector = BeanUtils.instantiateClass(Class.forName(candidate), ImportSelector.class);
|
||||
ImportSelector selector = BeanUtils.instantiateClass(
|
||||
this.resourceLoader.getClassLoader().loadClass(candidate), ImportSelector.class);
|
||||
processImport(configClass, selector.selectImports(importingClassMetadata), false);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
@@ -327,7 +327,8 @@ class ConfigurationClassParser {
|
||||
else if (new AssignableTypeFilter(ImportBeanDefinitionRegistrar.class).match(reader, metadataReaderFactory)) {
|
||||
// the candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions
|
||||
try {
|
||||
ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(Class.forName(candidate), ImportBeanDefinitionRegistrar.class);
|
||||
ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(
|
||||
this.resourceLoader.getClassLoader().loadClass(candidate), ImportBeanDefinitionRegistrar.class);
|
||||
registrar.registerBeanDefinitions(importingClassMetadata, registry);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
@@ -439,5 +440,4 @@ class ConfigurationClassParser {
|
||||
new Location(importStack.peek().getResource(), metadata));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -53,7 +53,8 @@ import java.lang.annotation.Target;
|
||||
public @interface Import {
|
||||
|
||||
/**
|
||||
* The @{@link Configuration} and/or {@link ImportSelector} classes to import.
|
||||
* The @{@link Configuration}, {@link ImportSelector} and/or
|
||||
* {@link ImportBeanDefinitionRegistrar} classes to import.
|
||||
*/
|
||||
Class<?>[] value();
|
||||
}
|
||||
|
||||
+23
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -113,7 +113,9 @@ public abstract class AbstractApplicationEventMulticaster implements Application
|
||||
* @see org.springframework.context.ApplicationListener
|
||||
*/
|
||||
protected Collection<ApplicationListener> getApplicationListeners() {
|
||||
return this.defaultRetriever.getApplicationListeners();
|
||||
synchronized (this.defaultRetriever) {
|
||||
return this.defaultRetriever.getApplicationListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,26 +137,30 @@ public abstract class AbstractApplicationEventMulticaster implements Application
|
||||
else {
|
||||
retriever = new ListenerRetriever(true);
|
||||
LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
|
||||
Set<ApplicationListener> listeners;
|
||||
Set<String> listenerBeans;
|
||||
synchronized (this.defaultRetriever) {
|
||||
for (ApplicationListener listener : this.defaultRetriever.applicationListeners) {
|
||||
if (supportsEvent(listener, eventType, sourceType)) {
|
||||
retriever.applicationListeners.add(listener);
|
||||
listeners = new LinkedHashSet<ApplicationListener>(this.defaultRetriever.applicationListeners);
|
||||
listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
|
||||
}
|
||||
for (ApplicationListener listener : listeners) {
|
||||
if (supportsEvent(listener, eventType, sourceType)) {
|
||||
retriever.applicationListeners.add(listener);
|
||||
allListeners.add(listener);
|
||||
}
|
||||
}
|
||||
if (!listenerBeans.isEmpty()) {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
for (String listenerBeanName : listenerBeans) {
|
||||
ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
|
||||
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
|
||||
retriever.applicationListenerBeans.add(listenerBeanName);
|
||||
allListeners.add(listener);
|
||||
}
|
||||
}
|
||||
if (!this.defaultRetriever.applicationListenerBeans.isEmpty()) {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
for (String listenerBeanName : this.defaultRetriever.applicationListenerBeans) {
|
||||
ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
|
||||
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
|
||||
retriever.applicationListenerBeans.add(listenerBeanName);
|
||||
allListeners.add(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
OrderComparator.sort(allListeners);
|
||||
this.retrieverCache.put(cacheKey, retriever);
|
||||
}
|
||||
OrderComparator.sort(allListeners);
|
||||
this.retrieverCache.put(cacheKey, retriever);
|
||||
return allListeners;
|
||||
}
|
||||
}
|
||||
|
||||
+25
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -67,12 +67,12 @@ public class MessageSourceResourceBundle extends ResourceBundle {
|
||||
|
||||
/**
|
||||
* This implementation resolves the code in the MessageSource.
|
||||
* Returns <code>null</code> if the message could not be resolved.
|
||||
* Returns {@code null} if the message could not be resolved.
|
||||
*/
|
||||
@Override
|
||||
protected Object handleGetObject(String code) {
|
||||
protected Object handleGetObject(String key) {
|
||||
try {
|
||||
return this.messageSource.getMessage(code, null, this.locale);
|
||||
return this.messageSource.getMessage(key, null, this.locale);
|
||||
}
|
||||
catch (NoSuchMessageException ex) {
|
||||
return null;
|
||||
@@ -80,17 +80,34 @@ public class MessageSourceResourceBundle extends ResourceBundle {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation returns <code>null</code>, as a MessageSource does
|
||||
* not allow for enumerating the defined message codes.
|
||||
* This implementation checks whether the target MessageSource can resolve
|
||||
* a message for the given key, translating {@code NoSuchMessageException}
|
||||
* accordingly. In contrast to ResourceBundle's default implementation in
|
||||
* JDK 1.6, this does not rely on the capability to enumerate message keys.
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKey(String key) {
|
||||
try {
|
||||
this.messageSource.getMessage(key, null, this.locale);
|
||||
return true;
|
||||
}
|
||||
catch (NoSuchMessageException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation throws {@code UnsupportedOperationException},
|
||||
* as a MessageSource does not allow for enumerating the defined message codes.
|
||||
*/
|
||||
@Override
|
||||
public Enumeration<String> getKeys() {
|
||||
return null;
|
||||
throw new UnsupportedOperationException("MessageSourceResourceBundle does not support enumerating its keys");
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation exposes the specified Locale for introspection
|
||||
* through the standard <code>ResourceBundle.getLocale()</code> method.
|
||||
* through the standard {@code ResourceBundle.getLocale()} method.
|
||||
*/
|
||||
@Override
|
||||
public Locale getLocale() {
|
||||
|
||||
+16
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -34,13 +34,13 @@ import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
|
||||
|
||||
/**
|
||||
* EJB3-compliant interceptor class that injects Spring beans into
|
||||
* fields and methods which are annotated with <code>@Autowired</code>.
|
||||
* fields and methods which are annotated with {@code @Autowired}.
|
||||
* Performs injection after construction as well as after activation
|
||||
* of a passivated bean.
|
||||
*
|
||||
* <p>To be applied through an <code>@Interceptors</code> annotation in
|
||||
* <p>To be applied through an {@code @Interceptors} annotation in
|
||||
* the EJB Session Bean or Message-Driven Bean class, or through an
|
||||
* <code>interceptor-binding</code> XML element in the EJB deployment descriptor.
|
||||
* {@code interceptor-binding} XML element in the EJB deployment descriptor.
|
||||
*
|
||||
* <p>Delegates to Spring's {@link AutowiredAnnotationBeanPostProcessor}
|
||||
* underneath, allowing for customization of its specific settings through
|
||||
@@ -60,7 +60,7 @@ import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
|
||||
*
|
||||
* <p><b>WARNING: Do not define the same bean as Spring-managed bean and as
|
||||
* EJB3 session bean in the same deployment unit.</b> In particular, be
|
||||
* careful when using the <code><context:component-scan></code> feature
|
||||
* careful when using the {@code <context:component-scan>} feature
|
||||
* in combination with the deployment of Spring-based EJB3 session beans:
|
||||
* Make sure that the EJB3 session beans are <i>not</i> autodetected as
|
||||
* Spring-managed beans as well, using appropriate package restrictions.
|
||||
@@ -71,8 +71,6 @@ import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
|
||||
* @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
|
||||
* @see org.springframework.context.access.ContextSingletonBeanFactoryLocator
|
||||
* @see #getBeanFactoryLocatorKey
|
||||
* @see org.springframework.ejb.support.AbstractEnterpriseBean#setBeanFactoryLocator
|
||||
* @see org.springframework.ejb.support.AbstractEnterpriseBean#setBeanFactoryLocatorKey
|
||||
*/
|
||||
public class SpringBeanAutowiringInterceptor {
|
||||
|
||||
@@ -99,9 +97,15 @@ public class SpringBeanAutowiringInterceptor {
|
||||
invocationContext.proceed();
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
doReleaseBean(invocationContext.getTarget());
|
||||
throw ex;
|
||||
}
|
||||
catch (Error err) {
|
||||
doReleaseBean(invocationContext.getTarget());
|
||||
throw err;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
doReleaseBean(invocationContext.getTarget());
|
||||
// Cannot declare a checked exception on WebSphere here - so we need to wrap.
|
||||
throw new EJBException(ex);
|
||||
}
|
||||
@@ -130,7 +134,7 @@ public class SpringBeanAutowiringInterceptor {
|
||||
/**
|
||||
* Determine the BeanFactory for autowiring the given target bean.
|
||||
* @param target the target bean to autowire
|
||||
* @return the BeanFactory to use (never <code>null</code>)
|
||||
* @return the BeanFactory to use (never {@code null})
|
||||
* @see #getBeanFactoryReference
|
||||
*/
|
||||
protected BeanFactory getBeanFactory(Object target) {
|
||||
@@ -146,7 +150,7 @@ public class SpringBeanAutowiringInterceptor {
|
||||
* <p>The default implementation delegates to {@link #getBeanFactoryLocator}
|
||||
* and {@link #getBeanFactoryLocatorKey}.
|
||||
* @param target the target bean to autowire
|
||||
* @return the BeanFactoryReference to use (never <code>null</code>)
|
||||
* @return the BeanFactoryReference to use (never {@code null})
|
||||
* @see #getBeanFactoryLocator
|
||||
* @see #getBeanFactoryLocatorKey
|
||||
* @see org.springframework.beans.factory.access.BeanFactoryLocator#useBeanFactory(String)
|
||||
@@ -163,7 +167,7 @@ public class SpringBeanAutowiringInterceptor {
|
||||
* <p>The default implementation exposes Spring's default
|
||||
* {@link ContextSingletonBeanFactoryLocator}.
|
||||
* @param target the target bean to autowire
|
||||
* @return the BeanFactoryLocator to use (never <code>null</code>)
|
||||
* @return the BeanFactoryLocator to use (never {@code null})
|
||||
* @see org.springframework.context.access.ContextSingletonBeanFactoryLocator#getInstance()
|
||||
*/
|
||||
protected BeanFactoryLocator getBeanFactoryLocator(Object target) {
|
||||
@@ -174,11 +178,11 @@ public class SpringBeanAutowiringInterceptor {
|
||||
* Determine the BeanFactoryLocator key to use. This typically indicates
|
||||
* the bean name of the ApplicationContext definition in
|
||||
* <strong>classpath*:beanRefContext.xml</strong> resource files.
|
||||
* <p>The default is <code>null</code>, indicating the single
|
||||
* <p>The default is {@code null}, indicating the single
|
||||
* ApplicationContext defined in the locator. This must be overridden
|
||||
* if more than one shared ApplicationContext definition is available.
|
||||
* @param target the target bean to autowire
|
||||
* @return the BeanFactoryLocator key to use (or <code>null</code> for
|
||||
* @return the BeanFactoryLocator key to use (or {@code null} for
|
||||
* referring to the single ApplicationContext defined in the locator)
|
||||
*/
|
||||
protected String getBeanFactoryLocatorKey(Object target) {
|
||||
|
||||
+5
-5
@@ -3,20 +3,20 @@
|
||||
*
|
||||
* <p>Base classes to make implementing EJB 2.x beans simpler and less error-prone,
|
||||
* as well as guaranteeing a Spring BeanFactory is available to such EJBs.
|
||||
* This promotes good practice, with EJB services used for transaction
|
||||
* This promotes good EJB practice, with EJB services used for transaction
|
||||
* management, thread management, and (possibly) remoting, while
|
||||
* business logic is implemented in easily testable POJOs.</p>
|
||||
*
|
||||
*
|
||||
* <p>In this model, the EJB is a facade, with as many POJO helpers
|
||||
* behind the BeanFactory as required.</p>
|
||||
*
|
||||
* <p>Note that the default behavior is to look for an EJB enviroment variable
|
||||
*
|
||||
* <p>Note that the default behavior is to look for an EJB environment variable
|
||||
* with name <code>ejb/BeanFactoryPath</code> that specifies the
|
||||
* location <i>on the classpath</i> of an XML bean factory definition
|
||||
* file (such as <code>/com/mycom/mypackage/mybeans.xml</code>).
|
||||
* If this JNDI key is missing, your EJB subclass won't successfully
|
||||
* initialize in the container.</p>
|
||||
*
|
||||
*
|
||||
* <p><b>Check out the <code>org.springframework.ejb.interceptor</code>
|
||||
* package for equivalent support for the EJB 3 component model</b>,
|
||||
* providing annotation-based autowiring using an EJB 3 interceptor.</p>
|
||||
|
||||
+14
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,12 +24,15 @@ import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link LoadTimeWeaver} implementation for OC4J's instrumentable ClassLoader.
|
||||
*
|
||||
* <p><b>NOTE:</b> Requires Oracle OC4J version 10.1.3.1 or higher.
|
||||
* Requires Oracle OC4J version 10.1.3.1 or higher.
|
||||
*
|
||||
* <p>Many thanks to <a href="mailto:mike.keith@oracle.com">Mike Keith</a>
|
||||
* for his assistance.
|
||||
*
|
||||
* <p><b>NOTE:</b> This class will be deprecated as of Spring 3.2, in favor of
|
||||
* {@link org.springframework.instrument.classloading.weblogic.WebLogicLoadTimeWeaver}
|
||||
* since Oracle end-of-lifed OC4J in favor of WebLogic.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
@@ -38,10 +41,11 @@ public class OC4JLoadTimeWeaver implements LoadTimeWeaver {
|
||||
|
||||
private final OC4JClassLoaderAdapter classLoader;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of thie {@link OC4JLoadTimeWeaver} class
|
||||
* using the default {@link ClassLoader class loader}.
|
||||
* @see org.springframework.util.ClassUtils#getDefaultClassLoader()
|
||||
* @see org.springframework.util.ClassUtils#getDefaultClassLoader()
|
||||
*/
|
||||
public OC4JLoadTimeWeaver() {
|
||||
this(ClassUtils.getDefaultClassLoader());
|
||||
@@ -57,18 +61,20 @@ public class OC4JLoadTimeWeaver implements LoadTimeWeaver {
|
||||
this.classLoader = new OC4JClassLoaderAdapter(classLoader);
|
||||
}
|
||||
|
||||
|
||||
public void addTransformer(ClassFileTransformer transformer) {
|
||||
Assert.notNull(transformer, "Transformer must not be null");
|
||||
// Since OC4J 10.1.3's PolicyClassLoader is going to be removed,
|
||||
// we rely on the ClassLoaderUtilities API instead.
|
||||
classLoader.addTransformer(transformer);
|
||||
this.classLoader.addTransformer(transformer);
|
||||
}
|
||||
|
||||
public ClassLoader getInstrumentableClassLoader() {
|
||||
return classLoader.getClassLoader();
|
||||
return this.classLoader.getClassLoader();
|
||||
}
|
||||
|
||||
public ClassLoader getThrowawayClassLoader() {
|
||||
return classLoader.getThrowawayClassLoader();
|
||||
return this.classLoader.getThrowawayClassLoader();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+11
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.jmx.support;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import javax.management.InstanceAlreadyExistsException;
|
||||
@@ -92,19 +93,19 @@ public class MBeanRegistrationSupport {
|
||||
private static final Constants constants = new Constants(MBeanRegistrationSupport.class);
|
||||
|
||||
/**
|
||||
* <code>Log</code> instance for this class.
|
||||
* {@code Log} instance for this class.
|
||||
*/
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
/**
|
||||
* The <code>MBeanServer</code> instance being used to register beans.
|
||||
* The {@code MBeanServer} instance being used to register beans.
|
||||
*/
|
||||
protected MBeanServer server;
|
||||
|
||||
/**
|
||||
* The beans that have been registered by this exporter.
|
||||
*/
|
||||
protected final Set<ObjectName> registeredBeans = new LinkedHashSet<ObjectName>();
|
||||
protected final Set<ObjectName> registeredBeans = Collections.synchronizedSet(new LinkedHashSet<ObjectName>());
|
||||
|
||||
/**
|
||||
* The action take when registering an MBean and finding that it already exists.
|
||||
@@ -114,16 +115,16 @@ public class MBeanRegistrationSupport {
|
||||
|
||||
|
||||
/**
|
||||
* Specify the <code>MBeanServer</code> instance with which all beans should
|
||||
* be registered. The <code>MBeanExporter</code> will attempt to locate an
|
||||
* existing <code>MBeanServer</code> if none is supplied.
|
||||
* Specify the {@code MBeanServer} instance with which all beans should
|
||||
* be registered. The {@code MBeanExporter} will attempt to locate an
|
||||
* existing {@code MBeanServer} if none is supplied.
|
||||
*/
|
||||
public void setServer(MBeanServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the <code>MBeanServer</code> that the beans will be registered with.
|
||||
* Return the {@code MBeanServer} that the beans will be registered with.
|
||||
*/
|
||||
public final MBeanServer getServer() {
|
||||
return this.server;
|
||||
@@ -205,10 +206,9 @@ public class MBeanRegistrationSupport {
|
||||
* Unregisters all beans that have been registered by an instance of this class.
|
||||
*/
|
||||
protected void unregisterBeans() {
|
||||
for (ObjectName objectName : this.registeredBeans) {
|
||||
for (ObjectName objectName : new LinkedHashSet<ObjectName>(this.registeredBeans)) {
|
||||
doUnregister(objectName);
|
||||
}
|
||||
this.registeredBeans.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -234,6 +234,7 @@ public class MBeanRegistrationSupport {
|
||||
logger.error("Could not unregister MBean [" + objectName + "]", ex);
|
||||
}
|
||||
}
|
||||
this.registeredBeans.remove(objectName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+27
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,7 +19,7 @@ package org.springframework.remoting.rmi;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.rmi.RemoteException;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.NamingException;
|
||||
import javax.rmi.PortableRemoteObject;
|
||||
|
||||
@@ -54,7 +54,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* Spring's unchecked RemoteAccessException.
|
||||
*
|
||||
* <p>The JNDI environment can be specified as "jndiEnvironment" property,
|
||||
* or be configured in a <code>jndi.properties</code> file or as system properties.
|
||||
* or be configured in a {@code jndi.properties} file or as system properties.
|
||||
* For example:
|
||||
*
|
||||
* <pre class="code"><property name="jndiEnvironment">
|
||||
@@ -88,6 +88,8 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho
|
||||
|
||||
private boolean refreshStubOnConnectFailure = false;
|
||||
|
||||
private boolean exposeAccessContext = false;
|
||||
|
||||
private Object cachedStub;
|
||||
|
||||
private final Object stubMonitor = new Object();
|
||||
@@ -166,6 +168,18 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho
|
||||
this.refreshStubOnConnectFailure = refreshStubOnConnectFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to expose the JNDI environment context for all access to the target
|
||||
* RMI stub, i.e. for all method invocations on the exposed object reference.
|
||||
* <p>Default is "false", i.e. to only expose the JNDI context for object lookup.
|
||||
* Switch this flag to "true" in order to expose the JNDI environment (including
|
||||
* the authorization context) for each RMI invocation, as needed by WebLogic
|
||||
* for RMI stubs with authorization requirements.
|
||||
*/
|
||||
public void setExposeAccessContext(boolean exposeAccessContext) {
|
||||
this.exposeAccessContext = exposeAccessContext;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws NamingException {
|
||||
@@ -190,8 +204,8 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho
|
||||
else if (getServiceInterface() != null) {
|
||||
boolean isImpl = getServiceInterface().isInstance(remoteObj);
|
||||
logger.debug("Using service interface [" + getServiceInterface().getName() +
|
||||
"] for JNDI RMI object [" + getJndiName() + "] - " +
|
||||
(!isImpl ? "not " : "") + "directly implemented");
|
||||
"] for JNDI RMI object [" + getJndiName() + "] - " +
|
||||
(!isImpl ? "not " : "") + "directly implemented");
|
||||
}
|
||||
}
|
||||
if (this.cacheStub) {
|
||||
@@ -268,13 +282,15 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho
|
||||
* @see java.rmi.NoSuchObjectException
|
||||
*/
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
Object stub = null;
|
||||
Object stub;
|
||||
try {
|
||||
stub = getStub();
|
||||
}
|
||||
catch (NamingException ex) {
|
||||
throw new RemoteLookupFailureException("JNDI lookup for RMI service [" + getJndiName() + "] failed", ex);
|
||||
}
|
||||
|
||||
Context ctx = (this.exposeAccessContext ? getJndiTemplate().getContext() : null);
|
||||
try {
|
||||
return doInvoke(invocation, stub);
|
||||
}
|
||||
@@ -297,6 +313,9 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
getJndiTemplate().releaseContext(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -354,7 +373,7 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho
|
||||
* @see #invoke
|
||||
*/
|
||||
protected Object refreshAndRetry(MethodInvocation invocation) throws Throwable {
|
||||
Object freshStub = null;
|
||||
Object freshStub;
|
||||
synchronized (this.stubMonitor) {
|
||||
this.cachedStub = null;
|
||||
freshStub = lookupStub();
|
||||
@@ -426,7 +445,7 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho
|
||||
* @see org.springframework.remoting.support.RemoteInvocation
|
||||
*/
|
||||
protected Object doInvoke(MethodInvocation methodInvocation, RmiInvocationHandler invocationHandler)
|
||||
throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
|
||||
if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {
|
||||
return "RMI invoker proxy for service URL [" + getJndiName() + "]";
|
||||
|
||||
+33
@@ -52,6 +52,39 @@
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="mode" default="proxy">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Should annotated beans be proxied using Spring's AOP framework,
|
||||
or should they rather be weaved with an AspectJ async execution aspect?
|
||||
|
||||
AspectJ weaving requires spring-aspects.jar on the classpath,
|
||||
as well as load-time weaving (or compile-time weaving) enabled.
|
||||
|
||||
Note: The weaving-based aspect requires the @Async annotation to be
|
||||
defined on the concrete class. Annotations in interfaces will not work
|
||||
in that case (they will rather only work with interface-based proxies)!
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="proxy"/>
|
||||
<xsd:enumeration value="aspectj"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Are class-based (CGLIB) proxies to be created? By default, standard
|
||||
Java interface-based proxies are created.
|
||||
|
||||
Note: Class-based proxies require the @Async annotation to be defined
|
||||
on the concrete class. Annotations in interfaces will not work in
|
||||
that case (they will rather only work with interface-based proxies)!
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
/**
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class AnnotationCacheOperationSourceTest {
|
||||
public class AnnotationCacheOperationSourceTests {
|
||||
|
||||
private AnnotationCacheOperationSource source = new AnnotationCacheOperationSource();
|
||||
|
||||
+53
@@ -25,10 +25,14 @@ import org.junit.Test;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
@@ -77,6 +81,24 @@ public class ImportAwareTests {
|
||||
assertThat(foo, is("xyz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void importRegistrar() throws Exception {
|
||||
ImportedRegistrar.called = false;
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ImportingRegistrarConfig.class);
|
||||
ctx.refresh();
|
||||
assertNotNull(ctx.getBean("registrarImportedBean"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void importRegistrarWithImport() throws Exception {
|
||||
ImportedRegistrar.called = false;
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ImportingRegistrarConfigWithImport.class);
|
||||
ctx.refresh();
|
||||
assertNotNull(ctx.getBean("registrarImportedBean"));
|
||||
assertNotNull(ctx.getBean(ImportedConfig.class));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(ImportedConfig.class)
|
||||
@@ -131,4 +153,35 @@ public class ImportAwareTests {
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableImportRegistrar
|
||||
static class ImportingRegistrarConfig {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableImportRegistrar
|
||||
@Import(ImportedConfig.class)
|
||||
static class ImportingRegistrarConfigWithImport {
|
||||
}
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Import(ImportedRegistrar.class)
|
||||
public @interface EnableImportRegistrar {
|
||||
}
|
||||
|
||||
static class ImportedRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
|
||||
static boolean called;
|
||||
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
|
||||
BeanDefinitionRegistry registry) {
|
||||
BeanDefinition beanDefinition = new GenericBeanDefinition();
|
||||
beanDefinition.setBeanClassName(String.class.getName());
|
||||
registry.registerBeanDefinition("registrarImportedBean", beanDefinition );
|
||||
Assert.state(called == false, "ImportedRegistrar called twice");
|
||||
called = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -115,8 +115,8 @@ public class ResourceBundleMessageSourceTests extends TestCase {
|
||||
if (alwaysUseMessageFormat) {
|
||||
pvs.add("alwaysUseMessageFormat", Boolean.TRUE);
|
||||
}
|
||||
Class clazz = reloadable ?
|
||||
(Class) ReloadableResourceBundleMessageSource.class : ResourceBundleMessageSource.class;
|
||||
Class<?> clazz = reloadable ?
|
||||
(Class<?>) ReloadableResourceBundleMessageSource.class : ResourceBundleMessageSource.class;
|
||||
ac.registerSingleton("messageSource", clazz, pvs);
|
||||
ac.refresh();
|
||||
|
||||
@@ -340,6 +340,17 @@ public class ResourceBundleMessageSourceTests extends TestCase {
|
||||
assertEquals(0, filenames.size());
|
||||
}
|
||||
|
||||
public void testMessageSourceResourceBundle() {
|
||||
ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
|
||||
ms.setBasename("org/springframework/context/support/messages");
|
||||
MessageSourceResourceBundle rbe = new MessageSourceResourceBundle(ms, Locale.ENGLISH);
|
||||
assertEquals("message1", rbe.getString("code1"));
|
||||
assertTrue(rbe.containsKey("code1"));
|
||||
MessageSourceResourceBundle rbg = new MessageSourceResourceBundle(ms, Locale.GERMAN);
|
||||
assertEquals("nachricht2", rbg.getString("code2"));
|
||||
assertTrue(rbg.containsKey("code2"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
if (JdkVersion.getMajorJavaVersion() >= JdkVersion.JAVA_16) {
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+7
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,7 +25,6 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
@@ -91,7 +90,7 @@ public abstract class BridgeMethodResolver {
|
||||
* Searches for the bridged method in the given candidates.
|
||||
* @param candidateMethods the List of candidate Methods
|
||||
* @param bridgeMethod the bridge method
|
||||
* @return the bridged method, or <code>null</code> if none found
|
||||
* @return the bridged method, or {@code null} if none found
|
||||
*/
|
||||
private static Method searchCandidates(List<Method> candidateMethods, Method bridgeMethod) {
|
||||
if (candidateMethods.isEmpty()) {
|
||||
@@ -114,7 +113,7 @@ public abstract class BridgeMethodResolver {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the supplied '<code>candidateMethod</code>' can be
|
||||
* Returns {@code true} if the supplied '{@code candidateMethod}' can be
|
||||
* consider a validate candidate for the {@link Method} that is {@link Method#isBridge() bridged}
|
||||
* by the supplied {@link Method bridge Method}. This method performs inexpensive
|
||||
* checks and can be used quickly filter for a set of possible matches.
|
||||
@@ -145,7 +144,7 @@ public abstract class BridgeMethodResolver {
|
||||
private static Method findGenericDeclaration(Method bridgeMethod) {
|
||||
// Search parent types for method that has same signature as bridge.
|
||||
Class superclass = bridgeMethod.getDeclaringClass().getSuperclass();
|
||||
while (!Object.class.equals(superclass)) {
|
||||
while (superclass != null && !Object.class.equals(superclass)) {
|
||||
Method method = searchForMatch(superclass, bridgeMethod);
|
||||
if (method != null && !method.isBridge()) {
|
||||
return method;
|
||||
@@ -166,10 +165,10 @@ public abstract class BridgeMethodResolver {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the {@link Type} signature of both the supplied
|
||||
* Returns {@code true} if the {@link Type} signature of both the supplied
|
||||
* {@link Method#getGenericParameterTypes() generic Method} and concrete {@link Method}
|
||||
* are equal after resolving all {@link TypeVariable TypeVariables} using the supplied
|
||||
* TypeVariable Map, otherwise returns <code>false</code>.
|
||||
* TypeVariable Map, otherwise returns {@code false}.
|
||||
*/
|
||||
private static boolean isResolvedTypeMatch(
|
||||
Method genericMethod, Method candidateMethod, Map<TypeVariable, Type> typeVariableMap) {
|
||||
@@ -205,7 +204,7 @@ public abstract class BridgeMethodResolver {
|
||||
/**
|
||||
* If the supplied {@link Class} has a declared {@link Method} whose signature matches
|
||||
* that of the supplied {@link Method}, then this matching {@link Method} is returned,
|
||||
* otherwise <code>null</code> is returned.
|
||||
* otherwise {@code null} is returned.
|
||||
*/
|
||||
private static Method searchForMatch(Class type, Method bridgeMethod) {
|
||||
return ReflectionUtils.findMethod(type, bridgeMethod.getName(), bridgeMethod.getParameterTypes());
|
||||
@@ -219,8 +218,6 @@ public abstract class BridgeMethodResolver {
|
||||
* @return whether signatures match as described
|
||||
*/
|
||||
public static boolean isVisibilityBridgeMethodPair(Method bridgeMethod, Method bridgedMethod) {
|
||||
Assert.isTrue(bridgeMethod != null);
|
||||
Assert.isTrue(bridgedMethod != null);
|
||||
if (bridgeMethod == bridgedMethod) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+8
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,12 +39,11 @@ import org.springframework.util.ClassUtils;
|
||||
/**
|
||||
* Implementation of {@link ParameterNameDiscoverer} that uses the LocalVariableTable
|
||||
* information in the method attributes to discover parameter names. Returns
|
||||
* <code>null</code> if the class file was compiled without debug information.
|
||||
* {@code null} if the class file was compiled without debug information.
|
||||
*
|
||||
* <p>Uses ObjectWeb's ASM library for analyzing class files. Each discoverer
|
||||
* instance caches the ASM discovered information for each introspected Class, in a
|
||||
* thread-safe manner. It is recommended to reuse discoverer instances
|
||||
* as far as possible.
|
||||
* <p>Uses ObjectWeb's ASM library for analyzing class files. Each discoverer instance
|
||||
* caches the ASM discovered information for each introspected Class, in a thread-safe
|
||||
* manner. It is recommended to reuse ParameterNameDiscoverer instances as far as possible.
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
* @author Costin Leau
|
||||
@@ -64,15 +63,15 @@ public class LocalVariableTableParameterNameDiscoverer implements ParameterNameD
|
||||
|
||||
|
||||
public String[] getParameterNames(Method method) {
|
||||
Class<?> declaringClass = method.getDeclaringClass();
|
||||
Method originalMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||
Class<?> declaringClass = originalMethod.getDeclaringClass();
|
||||
Map<Member, String[]> map = this.parameterNamesCache.get(declaringClass);
|
||||
if (map == null) {
|
||||
// initialize cache
|
||||
map = inspectClass(declaringClass);
|
||||
this.parameterNamesCache.put(declaringClass, map);
|
||||
}
|
||||
if (map != NO_DEBUG_INFO_MAP) {
|
||||
return map.get(method);
|
||||
return map.get(originalMethod);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -82,14 +81,12 @@ public class LocalVariableTableParameterNameDiscoverer implements ParameterNameD
|
||||
Class<?> declaringClass = ctor.getDeclaringClass();
|
||||
Map<Member, String[]> map = this.parameterNamesCache.get(declaringClass);
|
||||
if (map == null) {
|
||||
// initialize cache
|
||||
map = inspectClass(declaringClass);
|
||||
this.parameterNamesCache.put(declaringClass, map);
|
||||
}
|
||||
if (map != NO_DEBUG_INFO_MAP) {
|
||||
return map.get(ctor);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+35
-37
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -269,7 +269,6 @@ public class MethodParameter {
|
||||
* @param annotationType the annotation type to look for
|
||||
* @return the annotation object, or <code>null</code> if not found
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Annotation> T getMethodAnnotation(Class<T> annotationType) {
|
||||
return getAnnotatedElement().getAnnotation(annotationType);
|
||||
}
|
||||
@@ -311,14 +310,14 @@ public class MethodParameter {
|
||||
* Return true if the parameter has at least one annotation, false if it has none.
|
||||
*/
|
||||
public boolean hasParameterAnnotations() {
|
||||
return getParameterAnnotations().length != 0;
|
||||
return (getParameterAnnotations().length != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the parameter has the given annotation type, and false if it doesn't.
|
||||
*/
|
||||
public <T extends Annotation> boolean hasParameterAnnotation(Class<T> annotationType) {
|
||||
return getParameterAnnotation(annotationType) != null;
|
||||
return (getParameterAnnotation(annotationType) != null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -417,6 +416,38 @@ public class MethodParameter {
|
||||
return this.typeIndexesPerLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj != null && obj instanceof MethodParameter) {
|
||||
MethodParameter other = (MethodParameter) obj;
|
||||
|
||||
if (this.parameterIndex != other.parameterIndex) {
|
||||
return false;
|
||||
}
|
||||
else if (this.getMember().equals(other.getMember())) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.hash;
|
||||
if (result == 0) {
|
||||
result = getMember().hashCode();
|
||||
result = 31 * result + this.parameterIndex;
|
||||
this.hash = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MethodParameter for the given method or constructor.
|
||||
@@ -439,37 +470,4 @@ public class MethodParameter {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj != null && obj instanceof MethodParameter) {
|
||||
MethodParameter other = (MethodParameter) obj;
|
||||
|
||||
if (this.parameterIndex != other.parameterIndex) {
|
||||
return false;
|
||||
}
|
||||
else if (this.getMember().equals(other.getMember())) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.hash;
|
||||
if (result == 0) {
|
||||
result = getMember().hashCode();
|
||||
result = 31 * result + this.parameterIndex;
|
||||
this.hash = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-12
@@ -30,15 +30,15 @@ import org.springframework.util.StringUtils;
|
||||
* {@link Resource} implementation for class path resources.
|
||||
* Uses either a given ClassLoader or a given Class for loading resources.
|
||||
*
|
||||
* <p>Supports resolution as <code>java.io.File</code> if the class path
|
||||
* <p>Supports resolution as {@code java.io.File} if the class path
|
||||
* resource resides in the file system, but not for resources in a JAR.
|
||||
* Always supports resolution as URL.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 28.12.2003
|
||||
* @see java.lang.ClassLoader#getResourceAsStream(String)
|
||||
* @see java.lang.Class#getResourceAsStream(String)
|
||||
* @see ClassLoader#getResourceAsStream(String)
|
||||
* @see Class#getResourceAsStream(String)
|
||||
*/
|
||||
public class ClassPathResource extends AbstractFileResolvingResource {
|
||||
|
||||
@@ -69,8 +69,8 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
||||
* resource access methods will not accept it.
|
||||
* @param path the absolute path within the classpath
|
||||
* @param classLoader the class loader to load the resource with,
|
||||
* or <code>null</code> for the thread context class loader
|
||||
* @see java.lang.ClassLoader#getResourceAsStream(String)
|
||||
* or {@code null} for the thread context class loader
|
||||
* @see ClassLoader#getResourceAsStream(String)
|
||||
*/
|
||||
public ClassPathResource(String path, ClassLoader classLoader) {
|
||||
Assert.notNull(path, "Path must not be null");
|
||||
@@ -205,18 +205,14 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
||||
*/
|
||||
public String getDescription() {
|
||||
StringBuilder builder = new StringBuilder("class path resource [");
|
||||
|
||||
String pathToUse = path;
|
||||
|
||||
if (this.clazz != null && !pathToUse.startsWith("/")) {
|
||||
builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));
|
||||
builder.append('/');
|
||||
}
|
||||
|
||||
if (pathToUse.startsWith("/")) {
|
||||
pathToUse = pathToUse.substring(1);
|
||||
}
|
||||
|
||||
builder.append(pathToUse);
|
||||
builder.append(']');
|
||||
return builder.toString();
|
||||
@@ -232,9 +228,9 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
||||
}
|
||||
if (obj instanceof ClassPathResource) {
|
||||
ClassPathResource otherRes = (ClassPathResource) obj;
|
||||
return (this.path.equals(otherRes.path)
|
||||
&& ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) && ObjectUtils.nullSafeEquals(
|
||||
this.clazz, otherRes.clazz));
|
||||
return (this.path.equals(otherRes.path) &&
|
||||
ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) &&
|
||||
ObjectUtils.nullSafeEquals(this.clazz, otherRes.clazz));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
+13
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,7 +28,11 @@ import org.springframework.util.StringUtils;
|
||||
/**
|
||||
* Subclass of {@link PropertiesPropertySource} that loads a {@link Properties}
|
||||
* object from a given {@link org.springframework.core.io.Resource} or resource location such as
|
||||
* {@code "classpath:/com/myco/foo.properties"} or {@code "file:/path/to/file.properties"}.
|
||||
* {@code "classpath:/com/myco/foo.properties"} or {@code "file:/path/to/file.xml"}.
|
||||
* Both traditional and XML-based properties file formats are supported, however in order
|
||||
* for XML processing to take effect, the underlying {@code Resource}'s
|
||||
* {@link org.springframework.core.io.Resource#getFilename() getFilename()} method must
|
||||
* return non-{@code null} and end in ".xml".
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
@@ -99,7 +103,13 @@ public class ResourcePropertySource extends PropertiesPropertySource {
|
||||
private static Properties loadPropertiesForResource(Resource resource) throws IOException {
|
||||
Properties props = new Properties();
|
||||
InputStream is = resource.getInputStream();
|
||||
props.load(is);
|
||||
String filename = resource.getFilename();
|
||||
if (filename != null && filename.endsWith(".xml")) {
|
||||
props.loadFromXML(is);
|
||||
}
|
||||
else {
|
||||
props.load(is);
|
||||
}
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException ex) {
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -40,7 +40,7 @@ public class CachingMetadataReaderFactory extends SimpleMetadataReaderFactory {
|
||||
|
||||
private volatile int cacheLimit = DEFAULT_CACHE_LIMIT;
|
||||
|
||||
private final Map<Resource, MetadataReader> classReaderCache =
|
||||
private final Map<Resource, MetadataReader> metadataReaderCache =
|
||||
new LinkedHashMap<Resource, MetadataReader>(DEFAULT_CACHE_LIMIT, 0.75f, true) {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Resource, MetadataReader> eldest) {
|
||||
@@ -95,11 +95,11 @@ public class CachingMetadataReaderFactory extends SimpleMetadataReaderFactory {
|
||||
if (getCacheLimit() <= 0) {
|
||||
return super.getMetadataReader(resource);
|
||||
}
|
||||
synchronized (this.classReaderCache) {
|
||||
MetadataReader metadataReader = this.classReaderCache.get(resource);
|
||||
synchronized (this.metadataReaderCache) {
|
||||
MetadataReader metadataReader = this.metadataReaderCache.get(resource);
|
||||
if (metadataReader == null) {
|
||||
metadataReader = super.getMetadataReader(resource);
|
||||
this.classReaderCache.put(resource, metadataReader);
|
||||
this.metadataReaderCache.put(resource, metadataReader);
|
||||
}
|
||||
return metadataReader;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,14 +21,14 @@ package org.springframework.util;
|
||||
* errors that occur during asynchronous execution of tasks that have been
|
||||
* submitted to a TaskScheduler. In such cases, it may not be possible to
|
||||
* throw the error to the original caller.
|
||||
*
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 3.0.
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface ErrorHandler {
|
||||
|
||||
/**
|
||||
* Handle the given error, possibly rethrowing it as a fatal exception
|
||||
* Handle the given error, possibly rethrowing it as a fatal exception.
|
||||
*/
|
||||
void handleError(Throwable t);
|
||||
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -34,6 +34,7 @@ public class MethodParameterTests {
|
||||
|
||||
private MethodParameter intReturnType;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws NoSuchMethodException {
|
||||
Method method = getClass().getMethod("method", String.class, Long.TYPE);
|
||||
@@ -42,8 +43,6 @@ public class MethodParameterTests {
|
||||
intReturnType = new MethodParameter(method, -1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testEquals() throws NoSuchMethodException {
|
||||
assertEquals(stringParameter, stringParameter);
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
|
||||
<properties version="1.0">
|
||||
<entry key="foo">bar</entry>
|
||||
</properties>
|
||||
|
||||
+12
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,6 +39,10 @@ public class ResourcePropertySourceTests {
|
||||
private static final String PROPERTIES_LOCATION = "classpath:" + PROPERTIES_PATH;
|
||||
private static final String PROPERTIES_RESOURCE_DESCRIPTION = "class path resource [" + PROPERTIES_PATH + "]";
|
||||
|
||||
private static final String XML_PROPERTIES_PATH = "org/springframework/core/io/example.xml";
|
||||
private static final String XML_PROPERTIES_LOCATION = "classpath:" + XML_PROPERTIES_PATH;
|
||||
private static final String XML_PROPERTIES_RESOURCE_DESCRIPTION = "class path resource [" + XML_PROPERTIES_PATH + "]";
|
||||
|
||||
@Test
|
||||
public void withLocationAndGeneratedName() throws IOException {
|
||||
PropertySource<?> ps = new ResourcePropertySource(PROPERTIES_LOCATION);
|
||||
@@ -46,6 +50,13 @@ public class ResourcePropertySourceTests {
|
||||
assertThat(ps.getName(), is(PROPERTIES_RESOURCE_DESCRIPTION));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void xmlWithLocationAndGeneratedName() throws IOException {
|
||||
PropertySource<?> ps = new ResourcePropertySource(XML_PROPERTIES_LOCATION);
|
||||
assertEquals(ps.getProperty("foo"), "bar");
|
||||
assertThat(ps.getName(), is(XML_PROPERTIES_RESOURCE_DESCRIPTION));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withLocationAndExplicitName() throws IOException {
|
||||
PropertySource<?> ps = new ResourcePropertySource("ps1", PROPERTIES_LOCATION);
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-expression</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+137
-99
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,6 +21,8 @@ import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@@ -36,11 +38,13 @@ import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Simple PropertyAccessor that uses reflection to access properties for reading and writing. A property can be accessed
|
||||
* if it is accessible as a field on the object or through a getter (if being read) or a setter (if being written).
|
||||
*
|
||||
* Simple PropertyAccessor that uses reflection to access properties for reading and writing.
|
||||
* A property can be accessed if it is accessible as a field on the object or through a
|
||||
* getter (if being read) or a setter (if being written).
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
@@ -48,7 +52,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
protected final Map<CacheKey, InvokerPair> readerCache = new ConcurrentHashMap<CacheKey, InvokerPair>();
|
||||
|
||||
protected final Map<CacheKey, Member> writerCache = new ConcurrentHashMap<CacheKey, Member>();
|
||||
|
||||
|
||||
protected final Map<CacheKey, TypeDescriptor> typeDescriptorCache = new ConcurrentHashMap<CacheKey, TypeDescriptor>();
|
||||
|
||||
|
||||
@@ -85,7 +89,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
Field field = findField(name, type, target);
|
||||
if (field != null) {
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(field);
|
||||
this.readerCache.put(cacheKey, new InvokerPair(field,typeDescriptor));
|
||||
this.readerCache.put(cacheKey, new InvokerPair(field, typeDescriptor));
|
||||
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
|
||||
return true;
|
||||
}
|
||||
@@ -252,7 +256,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
|
||||
throw new AccessException("Neither setter nor field found for property '" + name + "'");
|
||||
}
|
||||
|
||||
|
||||
private TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
|
||||
if (target == null) {
|
||||
return null;
|
||||
@@ -263,15 +267,15 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
return TypeDescriptor.valueOf(Integer.TYPE);
|
||||
}
|
||||
CacheKey cacheKey = new CacheKey(type, name);
|
||||
TypeDescriptor typeDescriptor = this.typeDescriptorCache.get(cacheKey);
|
||||
TypeDescriptor typeDescriptor = this.typeDescriptorCache.get(cacheKey);
|
||||
if (typeDescriptor == null) {
|
||||
// attempt to populate the cache entry
|
||||
try {
|
||||
if (canRead(context, target, name)) {
|
||||
typeDescriptor = this.typeDescriptorCache.get(cacheKey);
|
||||
typeDescriptor = this.typeDescriptorCache.get(cacheKey);
|
||||
}
|
||||
else if (canWrite(context, target, name)) {
|
||||
typeDescriptor = this.typeDescriptorCache.get(cacheKey);
|
||||
typeDescriptor = this.typeDescriptorCache.get(cacheKey);
|
||||
}
|
||||
}
|
||||
catch (AccessException ex) {
|
||||
@@ -283,7 +287,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
|
||||
private Method findGetterForProperty(String propertyName, Class<?> clazz, Object target) {
|
||||
Method method = findGetterForProperty(propertyName, clazz, target instanceof Class);
|
||||
if(method == null && target instanceof Class) {
|
||||
if (method == null && target instanceof Class) {
|
||||
method = findGetterForProperty(propertyName, target.getClass(), false);
|
||||
}
|
||||
return method;
|
||||
@@ -291,7 +295,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
|
||||
private Method findSetterForProperty(String propertyName, Class<?> clazz, Object target) {
|
||||
Method method = findSetterForProperty(propertyName, clazz, target instanceof Class);
|
||||
if(method == null && target instanceof Class) {
|
||||
if (method == null && target instanceof Class) {
|
||||
method = findSetterForProperty(propertyName, target.getClass(), false);
|
||||
}
|
||||
return method;
|
||||
@@ -299,18 +303,19 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
|
||||
private Field findField(String name, Class<?> clazz, Object target) {
|
||||
Field field = findField(name, clazz, target instanceof Class);
|
||||
if(field == null && target instanceof Class) {
|
||||
if (field == null && target instanceof Class) {
|
||||
field = findField(name, target.getClass(), false);
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a getter method for the specified property. A getter is defined as a method whose name start with the prefix
|
||||
* 'get' and the rest of the name is the same as the property name (with the first character uppercased).
|
||||
* Find a getter method for the specified property.
|
||||
* <p>A getter is defined as a method whose name start with the prefix 'get' and the
|
||||
* rest of the name is the same as the property name (with the first character uppercased).
|
||||
*/
|
||||
protected Method findGetterForProperty(String propertyName, Class<?> clazz, boolean mustBeStatic) {
|
||||
Method[] ms = clazz.getMethods();
|
||||
Method[] ms = getSortedClassMethods(clazz);
|
||||
// Try "get*" method...
|
||||
String getterName = "get" + StringUtils.capitalize(propertyName);
|
||||
for (Method method : ms) {
|
||||
@@ -323,7 +328,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
getterName = "is" + StringUtils.capitalize(propertyName);
|
||||
for (Method method : ms) {
|
||||
if (method.getName().equals(getterName) && method.getParameterTypes().length == 0 &&
|
||||
boolean.class.equals(method.getReturnType()) &&
|
||||
(boolean.class.equals(method.getReturnType()) || Boolean.class.equals(method.getReturnType())) &&
|
||||
(!mustBeStatic || Modifier.isStatic(method.getModifiers()))) {
|
||||
return method;
|
||||
}
|
||||
@@ -335,7 +340,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
* Find a setter method for the specified property.
|
||||
*/
|
||||
protected Method findSetterForProperty(String propertyName, Class<?> clazz, boolean mustBeStatic) {
|
||||
Method[] methods = clazz.getMethods();
|
||||
Method[] methods = getSortedClassMethods(clazz);
|
||||
String setterName = "set" + StringUtils.capitalize(propertyName);
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals(setterName) && method.getParameterTypes().length == 1 &&
|
||||
@@ -346,6 +351,19 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns class methods ordered with non bridge methods appearing higher.
|
||||
*/
|
||||
private Method[] getSortedClassMethods(Class<?> clazz) {
|
||||
Method[] methods = clazz.getMethods();
|
||||
Arrays.sort(methods, new Comparator<Method>() {
|
||||
public int compare(Method o1, Method o2) {
|
||||
return (o1.isBridge() == o2.isBridge()) ? 0 : (o1.isBridge() ? 1 : -1);
|
||||
}
|
||||
});
|
||||
return methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a field of a certain name on a specified class
|
||||
*/
|
||||
@@ -356,58 +374,26 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
// We'll search superclasses and implemented interfaces explicitly,
|
||||
// although it shouldn't be necessary - however, see SPR-10125.
|
||||
if (clazz.getSuperclass() != null) {
|
||||
Field field = findField(name, clazz.getSuperclass(), mustBeStatic);
|
||||
if (field != null) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
for (Class<?> implementedInterface : clazz.getInterfaces()) {
|
||||
Field field = findField(name, implementedInterface, mustBeStatic);
|
||||
if (field != null) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Captures the member (method/field) to call reflectively to access a property value and the type descriptor for the
|
||||
* value returned by the reflective call.
|
||||
*/
|
||||
private static class InvokerPair {
|
||||
|
||||
final Member member;
|
||||
|
||||
final TypeDescriptor typeDescriptor;
|
||||
|
||||
public InvokerPair(Member member, TypeDescriptor typeDescriptor) {
|
||||
this.member = member;
|
||||
this.typeDescriptor = typeDescriptor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class CacheKey {
|
||||
|
||||
private final Class clazz;
|
||||
|
||||
private final String name;
|
||||
|
||||
public CacheKey(Class clazz, String name) {
|
||||
this.clazz = clazz;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof CacheKey)) {
|
||||
return false;
|
||||
}
|
||||
CacheKey otherKey = (CacheKey) other;
|
||||
return (this.clazz.equals(otherKey.clazz) && this.name.equals(otherKey.name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.clazz.hashCode() * 29 + this.name.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to create an optimized property accessor tailored for a property of a particular name on
|
||||
* a particular class. The general ReflectivePropertyAccessor will always work but is not optimal
|
||||
* a particular class. The general ReflectivePropertyAccessor will always work but is not optimal
|
||||
* due to the need to lookup which reflective member (method/field) to use each time read() is called.
|
||||
* This method will just return the ReflectivePropertyAccessor instance if it is unable to build
|
||||
* something more optimal.
|
||||
@@ -456,37 +442,90 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An optimized form of a PropertyAccessor that will use reflection but only knows how to access a particular property
|
||||
* on a particular class. This is unlike the general ReflectivePropertyResolver which manages a cache of methods/fields that
|
||||
* may be invoked to access different properties on different classes. This optimal accessor exists because looking up
|
||||
* the appropriate reflective object by class/name on each read is not cheap.
|
||||
* Captures the member (method/field) to call reflectively to access a property value
|
||||
* and the type descriptor for the value returned by the reflective call.
|
||||
*/
|
||||
static class OptimalPropertyAccessor implements PropertyAccessor {
|
||||
private static class InvokerPair {
|
||||
|
||||
final Member member;
|
||||
|
||||
final TypeDescriptor typeDescriptor;
|
||||
|
||||
public InvokerPair(Member member, TypeDescriptor typeDescriptor) {
|
||||
this.member = member;
|
||||
this.typeDescriptor = typeDescriptor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class CacheKey {
|
||||
|
||||
private final Class clazz;
|
||||
|
||||
private final String name;
|
||||
|
||||
public CacheKey(Class clazz, String name) {
|
||||
this.clazz = clazz;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof CacheKey)) {
|
||||
return false;
|
||||
}
|
||||
CacheKey otherKey = (CacheKey) other;
|
||||
return (this.clazz.equals(otherKey.clazz) && this.name.equals(otherKey.name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.clazz.hashCode() * 29 + this.name.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An optimized form of a PropertyAccessor that will use reflection but only knows
|
||||
* how to access a particular property on a particular class. This is unlike the
|
||||
* general ReflectivePropertyResolver which manages a cache of methods/fields that
|
||||
* may be invoked to access different properties on different classes. This optimal
|
||||
* accessor exists because looking up the appropriate reflective object by class/name
|
||||
* on each read is not cheap.
|
||||
*/
|
||||
private static class OptimalPropertyAccessor implements PropertyAccessor {
|
||||
|
||||
private final Member member;
|
||||
|
||||
private final TypeDescriptor typeDescriptor;
|
||||
|
||||
private final boolean needsToBeMadeAccessible;
|
||||
|
||||
|
||||
OptimalPropertyAccessor(InvokerPair target) {
|
||||
this.member = target.member;
|
||||
this.member = target.member;
|
||||
this.typeDescriptor = target.typeDescriptor;
|
||||
if (this.member instanceof Field) {
|
||||
Field field = (Field)member;
|
||||
needsToBeMadeAccessible = (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()))
|
||||
&& !field.isAccessible();
|
||||
Field field = (Field) this.member;
|
||||
this.needsToBeMadeAccessible = (!Modifier.isPublic(field.getModifiers()) ||
|
||||
!Modifier.isPublic(field.getDeclaringClass().getModifiers())) && !field.isAccessible();
|
||||
}
|
||||
else {
|
||||
Method method = (Method)member;
|
||||
needsToBeMadeAccessible = ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
|
||||
&& !method.isAccessible());
|
||||
Method method = (Method) this.member;
|
||||
this.needsToBeMadeAccessible = ((!Modifier.isPublic(method.getModifiers()) ||
|
||||
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible());
|
||||
}
|
||||
}
|
||||
|
||||
public Class[] getSpecificTargetClasses() {
|
||||
throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor");
|
||||
}
|
||||
|
||||
|
||||
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
if (target == null) {
|
||||
return false;
|
||||
@@ -495,8 +534,8 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
if (type.isArray()) {
|
||||
return false;
|
||||
}
|
||||
if (member instanceof Method) {
|
||||
Method method = (Method)member;
|
||||
if (this.member instanceof Method) {
|
||||
Method method = (Method) this.member;
|
||||
String getterName = "get" + StringUtils.capitalize(name);
|
||||
if (getterName.equals(method.getName())) {
|
||||
return true;
|
||||
@@ -505,31 +544,31 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
return getterName.equals(method.getName());
|
||||
}
|
||||
else {
|
||||
Field field = (Field)member;
|
||||
Field field = (Field) this.member;
|
||||
return field.getName().equals(name);
|
||||
}
|
||||
}
|
||||
|
||||
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
if (member instanceof Method) {
|
||||
if (this.member instanceof Method) {
|
||||
try {
|
||||
if (needsToBeMadeAccessible) {
|
||||
ReflectionUtils.makeAccessible((Method) member);
|
||||
if (this.needsToBeMadeAccessible) {
|
||||
ReflectionUtils.makeAccessible((Method) this.member);
|
||||
}
|
||||
Object value = ((Method) member).invoke(target);
|
||||
return new TypedValue(value, typeDescriptor.narrow(value));
|
||||
Object value = ((Method) this.member).invoke(target);
|
||||
return new TypedValue(value, this.typeDescriptor.narrow(value));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new AccessException("Unable to access property '" + name + "' through getter", ex);
|
||||
}
|
||||
}
|
||||
if (member instanceof Field) {
|
||||
}
|
||||
if (this.member instanceof Field) {
|
||||
try {
|
||||
if (needsToBeMadeAccessible) {
|
||||
ReflectionUtils.makeAccessible((Field)member);
|
||||
if (this.needsToBeMadeAccessible) {
|
||||
ReflectionUtils.makeAccessible((Field) this.member);
|
||||
}
|
||||
Object value = ((Field)member).get(target);
|
||||
return new TypedValue(value, typeDescriptor.narrow(value));
|
||||
Object value = ((Field) this.member).get(target);
|
||||
return new TypedValue(value, this.typeDescriptor.narrow(value));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new AccessException("Unable to access field: " + name, ex);
|
||||
@@ -538,12 +577,11 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
throw new AccessException("Neither getter nor field found for property '" + name + "'");
|
||||
}
|
||||
|
||||
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
public boolean canWrite(EvaluationContext context, Object target, String name) {
|
||||
throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor");
|
||||
}
|
||||
|
||||
public void write(EvaluationContext context, Object target, String name, Object newValue)
|
||||
throws AccessException {
|
||||
|
||||
public void write(EvaluationContext context, Object target, String name, Object newValue) {
|
||||
throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-instrument-tomcat</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-instrument</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
@@ -431,11 +431,11 @@
|
||||
<orderEntry type="module-library">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$IVY_CACHE$/org.hibernate/com.springsource.org.hibernate/3.3.1.GA/com.springsource.org.hibernate-3.3.1.GA.jar!/" />
|
||||
<root url="jar://$IVY_CACHE$/org.hibernate/com.springsource.org.hibernate/3.3.2.GA/com.springsource.org.hibernate-3.3.2.GA.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$IVY_CACHE$/org.hibernate/com.springsource.org.hibernate/3.3.1.GA/com.springsource.org.hibernate-sources-3.3.1.GA.jar!/" />
|
||||
<root url="jar://$IVY_CACHE$/org.hibernate/com.springsource.org.hibernate/3.3.2.GA/com.springsource.org.hibernate-sources-3.3.2.GA.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</orderEntry>
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
<dependency org="org.eclipse.persistence" name="com.springsource.org.eclipse.persistence" rev="1.0.1" conf="test->compile"/>
|
||||
<dependency org="org.eclipse.persistence" name="com.springsource.org.eclipse.persistence.jpa" rev="1.0.1" conf="test->compile"/>
|
||||
<dependency org="org.freemarker" name="com.springsource.freemarker" rev="2.3.15" conf="test->compile"/>
|
||||
<dependency org="org.hibernate" name="com.springsource.org.hibernate" rev="3.3.1.GA" conf="test->compile"/>
|
||||
<dependency org="org.hibernate" name="com.springsource.org.hibernate" rev="3.3.2.GA" conf="test->compile"/>
|
||||
<dependency org="org.hibernate" name="com.springsource.org.hibernate.annotations" rev="3.4.0.GA" conf="test->compile"/>
|
||||
<dependency org="org.hibernate" name="com.springsource.org.hibernate.annotations.common" rev="3.3.0.ga" conf="test->compile"/>
|
||||
<dependency org="org.hibernate" name="com.springsource.org.hibernate.ejb" rev="3.4.0.GA" conf="test->compile"/>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-integration-tests</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jdbc</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+10
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,7 +48,7 @@ public class PreparedStatementCreatorFactory {
|
||||
/** The SQL, which won't change when the parameters change */
|
||||
private final String sql;
|
||||
|
||||
/** List of SqlParameter objects. May not be <code>null</code>. */
|
||||
/** List of SqlParameter objects. May not be {@code null}. */
|
||||
private final List<SqlParameter> declaredParameters;
|
||||
|
||||
private int resultSetType = ResultSet.TYPE_FORWARD_ONLY;
|
||||
@@ -144,15 +144,15 @@ public class PreparedStatementCreatorFactory {
|
||||
|
||||
/**
|
||||
* Return a new PreparedStatementSetter for the given parameters.
|
||||
* @param params list of parameters (may be <code>null</code>)
|
||||
* @param params list of parameters (may be {@code null})
|
||||
*/
|
||||
public PreparedStatementSetter newPreparedStatementSetter(List params) {
|
||||
public PreparedStatementSetter newPreparedStatementSetter(List<?> params) {
|
||||
return new PreparedStatementCreatorImpl(params != null ? params : Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new PreparedStatementSetter for the given parameters.
|
||||
* @param params the parameter array (may be <code>null</code>)
|
||||
* @param params the parameter array (may be {@code null})
|
||||
*/
|
||||
public PreparedStatementSetter newPreparedStatementSetter(Object[] params) {
|
||||
return new PreparedStatementCreatorImpl(params != null ? Arrays.asList(params) : Collections.emptyList());
|
||||
@@ -160,7 +160,7 @@ public class PreparedStatementCreatorFactory {
|
||||
|
||||
/**
|
||||
* Return a new PreparedStatementCreator for the given parameters.
|
||||
* @param params list of parameters (may be <code>null</code>)
|
||||
* @param params list of parameters (may be {@code null})
|
||||
*/
|
||||
public PreparedStatementCreator newPreparedStatementCreator(List<?> params) {
|
||||
return new PreparedStatementCreatorImpl(params != null ? params : Collections.emptyList());
|
||||
@@ -168,7 +168,7 @@ public class PreparedStatementCreatorFactory {
|
||||
|
||||
/**
|
||||
* Return a new PreparedStatementCreator for the given parameters.
|
||||
* @param params the parameter array (may be <code>null</code>)
|
||||
* @param params the parameter array (may be {@code null})
|
||||
*/
|
||||
public PreparedStatementCreator newPreparedStatementCreator(Object[] params) {
|
||||
return new PreparedStatementCreatorImpl(params != null ? Arrays.asList(params) : Collections.emptyList());
|
||||
@@ -178,7 +178,7 @@ public class PreparedStatementCreatorFactory {
|
||||
* Return a new PreparedStatementCreator for the given parameters.
|
||||
* @param sqlToUse the actual SQL statement to use (if different from
|
||||
* the factory's, for example because of named parameter expanding)
|
||||
* @param params the parameter array (may be <code>null</code>)
|
||||
* @param params the parameter array (may be {@code null})
|
||||
*/
|
||||
public PreparedStatementCreator newPreparedStatementCreator(String sqlToUse, Object[] params) {
|
||||
return new PreparedStatementCreatorImpl(
|
||||
@@ -225,7 +225,7 @@ public class PreparedStatementCreatorFactory {
|
||||
}
|
||||
|
||||
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
|
||||
PreparedStatement ps = null;
|
||||
PreparedStatement ps;
|
||||
if (generatedKeysColumnNames != null || returnGeneratedKeys) {
|
||||
try {
|
||||
if (generatedKeysColumnNames != null) {
|
||||
@@ -263,7 +263,7 @@ public class PreparedStatementCreatorFactory {
|
||||
int sqlColIndx = 1;
|
||||
for (int i = 0; i < this.parameters.size(); i++) {
|
||||
Object in = this.parameters.get(i);
|
||||
SqlParameter declaredParameter = null;
|
||||
SqlParameter declaredParameter;
|
||||
// SqlParameterValue overrides declared parameter metadata, in particular for
|
||||
// independence from the declared parameter position in case of named parameters.
|
||||
if (in instanceof SqlParameterValue) {
|
||||
|
||||
+17
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,8 +28,8 @@ import org.springframework.jdbc.support.rowset.SqlRowSet;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* ResultSetExtractor implementation that returns a Spring SqlRowSet
|
||||
* representation for each given ResultSet.
|
||||
* {@link ResultSetExtractor} implementation that returns a Spring {@link SqlRowSet}
|
||||
* representation for each given {@link ResultSet}.
|
||||
*
|
||||
* <p>The default implementation uses a standard JDBC CachedRowSet underneath.
|
||||
* This means that JDBC RowSet support needs to be available at runtime:
|
||||
@@ -88,8 +88,9 @@ public class SqlRowSetResultSetExtractor implements ResultSetExtractor<SqlRowSet
|
||||
/**
|
||||
* Create a new CachedRowSet instance, to be populated by
|
||||
* the <code>createSqlRowSet</code> implementation.
|
||||
* <p>The default implementation creates a new instance of
|
||||
* Sun's <code>com.sun.rowset.CachedRowSetImpl</code> class.
|
||||
* <p>The default implementation uses JDBC 4.1's RowSetProvider
|
||||
* when running on JDK 7 or higher, falling back to Sun's
|
||||
* <code>com.sun.rowset.CachedRowSetImpl</code> class on older JDKs.
|
||||
* @return a new CachedRowSet instance
|
||||
* @throws SQLException if thrown by JDBC methods
|
||||
* @see #createSqlRowSet
|
||||
@@ -101,6 +102,17 @@ public class SqlRowSetResultSetExtractor implements ResultSetExtractor<SqlRowSet
|
||||
return (CachedRowSet) ReflectionUtils.invokeJdbcMethod(createCachedRowSet, rowSetFactory);
|
||||
}
|
||||
else {
|
||||
return SunCachedRowSetFactory.createCachedRowSet();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to avoid a hard dependency on Sun's CachedRowSetImpl class.
|
||||
*/
|
||||
private static class SunCachedRowSetFactory {
|
||||
|
||||
public static CachedRowSet createCachedRowSet() throws SQLException {
|
||||
return new CachedRowSetImpl();
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -93,7 +93,7 @@ public abstract class StatementCreatorUtils {
|
||||
/**
|
||||
* Derive a default SQL type from the given Java type.
|
||||
* @param javaType the Java type to translate
|
||||
* @return the corresponding SQL type, or <code>null</code> if none found
|
||||
* @return the corresponding SQL type, or {@code null} if none found
|
||||
*/
|
||||
public static int javaTypeToSqlParameterType(Class javaType) {
|
||||
Integer sqlType = javaTypeToSqlTypeMap.get(javaType);
|
||||
@@ -233,7 +233,7 @@ public abstract class StatementCreatorUtils {
|
||||
try {
|
||||
pmd = ps.getParameterMetaData();
|
||||
}
|
||||
catch (AbstractMethodError err) {
|
||||
catch (Throwable ex) {
|
||||
// JDBC driver not compliant with JDBC 3.0
|
||||
// -> proceed with database-specific checks
|
||||
}
|
||||
@@ -381,7 +381,7 @@ public abstract class StatementCreatorUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given value is a <code>java.util.Date</code>
|
||||
* Check whether the given value is a {@code java.util.Date}
|
||||
* (but not one of the JDBC-specific subclasses).
|
||||
*/
|
||||
private static boolean isDateValue(Class inValueType) {
|
||||
@@ -394,7 +394,7 @@ public abstract class StatementCreatorUtils {
|
||||
/**
|
||||
* Clean up all resources held by parameter values which were passed to an
|
||||
* execute method. This is for example important for closing LOB values.
|
||||
* @param paramValues parameter values supplied. May be <code>null</code>.
|
||||
* @param paramValues parameter values supplied. May be {@code null}.
|
||||
* @see DisposableSqlTypeValue#cleanup()
|
||||
* @see org.springframework.jdbc.core.support.SqlLobValue#cleanup()
|
||||
*/
|
||||
@@ -407,7 +407,7 @@ public abstract class StatementCreatorUtils {
|
||||
/**
|
||||
* Clean up all resources held by parameter values which were passed to an
|
||||
* execute method. This is for example important for closing LOB values.
|
||||
* @param paramValues parameter values supplied. May be <code>null</code>.
|
||||
* @param paramValues parameter values supplied. May be {@code null}.
|
||||
* @see DisposableSqlTypeValue#cleanup()
|
||||
* @see org.springframework.jdbc.core.support.SqlLobValue#cleanup()
|
||||
*/
|
||||
|
||||
+11
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -109,7 +109,7 @@ public abstract class AbstractDriverBasedDataSource extends AbstractDataSource {
|
||||
|
||||
|
||||
/**
|
||||
* This implementation delegates to <code>getConnectionFromDriver</code>,
|
||||
* This implementation delegates to {@code getConnectionFromDriver},
|
||||
* using the default username and password of this DataSource.
|
||||
* @see #getConnectionFromDriver(String, String)
|
||||
* @see #setUsername
|
||||
@@ -120,7 +120,7 @@ public abstract class AbstractDriverBasedDataSource extends AbstractDataSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation delegates to <code>getConnectionFromDriver</code>,
|
||||
* This implementation delegates to {@code getConnectionFromDriver},
|
||||
* using the given username and password.
|
||||
* @see #getConnectionFromDriver(String, String)
|
||||
*/
|
||||
@@ -139,14 +139,18 @@ public abstract class AbstractDriverBasedDataSource extends AbstractDataSource {
|
||||
* @see java.sql.Driver#connect(String, java.util.Properties)
|
||||
*/
|
||||
protected Connection getConnectionFromDriver(String username, String password) throws SQLException {
|
||||
Properties props = new Properties(getConnectionProperties());
|
||||
Properties mergedProps = new Properties();
|
||||
Properties connProps = getConnectionProperties();
|
||||
if (connProps != null) {
|
||||
mergedProps.putAll(connProps);
|
||||
}
|
||||
if (username != null) {
|
||||
props.setProperty("user", username);
|
||||
mergedProps.setProperty("user", username);
|
||||
}
|
||||
if (password != null) {
|
||||
props.setProperty("password", password);
|
||||
mergedProps.setProperty("password", password);
|
||||
}
|
||||
return getConnectionFromDriver(props);
|
||||
return getConnectionFromDriver(mergedProps);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+27
-21
@@ -118,10 +118,10 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate that a failed SQL <code>DROP</code> statement can be ignored.
|
||||
* Flag to indicate that a failed SQL {@code DROP} statement can be ignored.
|
||||
* <p>This is useful for non-embedded databases whose SQL dialect does not support an
|
||||
* <code>IF EXISTS</code> clause in a <code>DROP</code>. The default is false so that if the
|
||||
* populator runs accidentally, it will fail fast when the script starts with a <code>DROP</code>.
|
||||
* {@code IF EXISTS} clause in a {@code DROP}. The default is false so that if the
|
||||
* populator runs accidentally, it will fail fast when the script starts with a {@code DROP}.
|
||||
*/
|
||||
public void setIgnoreFailedDrops(boolean ignoreFailedDrops) {
|
||||
this.ignoreFailedDrops = ignoreFailedDrops;
|
||||
@@ -151,10 +151,10 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
|
||||
* @param connection the JDBC Connection with which to perform JDBC operations
|
||||
* @param resource the resource (potentially associated with a specific encoding) to load the SQL script from
|
||||
* @param continueOnError whether or not to continue without throwing an exception in the event of an error
|
||||
* @param ignoreFailedDrops whether of not to continue in the event of specifically an error on a <code>DROP</code>
|
||||
* @param ignoreFailedDrops whether of not to continue in the event of specifically an error on a {@code DROP}
|
||||
*/
|
||||
private void executeSqlScript(Connection connection, EncodedResource resource,
|
||||
boolean continueOnError, boolean ignoreFailedDrops) throws SQLException {
|
||||
private void executeSqlScript(Connection connection, EncodedResource resource, boolean continueOnError,
|
||||
boolean ignoreFailedDrops) throws SQLException {
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Executing SQL script from " + resource);
|
||||
@@ -219,25 +219,30 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
|
||||
/**
|
||||
* Read a script from the given resource and build a String containing the lines.
|
||||
* @param resource the resource to be read
|
||||
* @return <code>String</code> containing the script lines
|
||||
* @return {@code String} containing the script lines
|
||||
* @throws IOException in case of I/O errors
|
||||
*/
|
||||
private String readScript(EncodedResource resource) throws IOException {
|
||||
LineNumberReader lnr = new LineNumberReader(resource.getReader());
|
||||
String currentStatement = lnr.readLine();
|
||||
StringBuilder scriptBuilder = new StringBuilder();
|
||||
while (currentStatement != null) {
|
||||
if (StringUtils.hasText(currentStatement) &&
|
||||
(this.commentPrefix != null && !currentStatement.startsWith(this.commentPrefix))) {
|
||||
if (scriptBuilder.length() > 0) {
|
||||
scriptBuilder.append('\n');
|
||||
try {
|
||||
String currentStatement = lnr.readLine();
|
||||
StringBuilder scriptBuilder = new StringBuilder();
|
||||
while (currentStatement != null) {
|
||||
if (StringUtils.hasText(currentStatement) &&
|
||||
(this.commentPrefix != null && !currentStatement.startsWith(this.commentPrefix))) {
|
||||
if (scriptBuilder.length() > 0) {
|
||||
scriptBuilder.append('\n');
|
||||
}
|
||||
scriptBuilder.append(currentStatement);
|
||||
}
|
||||
scriptBuilder.append(currentStatement);
|
||||
currentStatement = lnr.readLine();
|
||||
}
|
||||
currentStatement = lnr.readLine();
|
||||
maybeAddSeparatorToScript(scriptBuilder);
|
||||
return scriptBuilder.toString();
|
||||
}
|
||||
finally {
|
||||
lnr.close();
|
||||
}
|
||||
maybeAddSeparatorToScript(scriptBuilder);
|
||||
return scriptBuilder.toString();
|
||||
}
|
||||
|
||||
private void maybeAddSeparatorToScript(StringBuilder scriptBuilder) {
|
||||
@@ -248,7 +253,8 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
|
||||
if (trimmed.length() == this.separator.length()) {
|
||||
return;
|
||||
}
|
||||
// separator ends in whitespace, so we might want to see if the script is trying to end the same way
|
||||
// separator ends in whitespace, so we might want to see if the script is trying
|
||||
// to end the same way
|
||||
if (scriptBuilder.lastIndexOf(trimmed) == scriptBuilder.length() - trimmed.length()) {
|
||||
scriptBuilder.append(this.separator.substring(trimmed.length()));
|
||||
}
|
||||
@@ -290,8 +296,8 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Split an SQL script into separate statements delimited with the provided delimiter
|
||||
* character. Each individual statement will be added to the provided {@code List}.
|
||||
* Split an SQL script into separate statements delimited by the provided delimiter
|
||||
* string. Each individual statement will be added to the provided {@code List}.
|
||||
* @param script the SQL script
|
||||
* @param delim character delimiting each statement (typically a ';' character)
|
||||
* @param statements the List that will contain the individual statements
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.apache.commons/com.springsource.org.apache.commons.pool/1.3.0/com.springsource.org.apache.commons.pool-1.3.0.jar" sourcepath="/IVY_CACHE/org.apache.commons/com.springsource.org.apache.commons.pool/1.3.0/com.springsource.org.apache.commons.pool-sources-1.3.0.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.codehaus.jackson/com.springsource.org.codehaus.jackson.mapper/1.4.2/com.springsource.org.codehaus.jackson.mapper-1.4.2.jar" sourcepath="/IVY_CACHE/org.codehaus.jackson/com.springsource.org.codehaus.jackson.mapper/1.4.2/com.springsource.org.codehaus.jackson.mapper-sources-1.4.2.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.codehaus.jackson/com.springsource.org.codehaus.jackson/1.4.2/com.springsource.org.codehaus.jackson-1.4.2.jar" sourcepath="/IVY_CACHE/org.codehaus.jackson/com.springsource.org.codehaus.jackson/1.4.2/com.springsource.org.codehaus.jackson-sources-1.4.2.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-databind/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-databind-2.0.2.jar" sourcepath="IVY_CACHE/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-databind/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-databind-2.0.2-sources.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-core/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-core-2.0.2.jar" sourcepath="IVY_CACHE/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-core/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-core-2.0.2-sources.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-annotations/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-annotations-2.0.2.jar" sourcepath="IVY_CACHE/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-annotations/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-annotations-2.0.2-sources.jar"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/org.springframework.oxm"/>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
<dependency org="org.junit" name="com.springsource.org.junit" rev="${junit.version}" conf="test->compile"/>
|
||||
<dependency org="org.codehaus.jackson" name="com.springsource.org.codehaus.jackson.mapper" rev="1.4.2"
|
||||
conf="optional, jackson->compile"/>
|
||||
<dependency org="com.fasterxml.jackson.core" name="com.springsource.com.fasterxml.jackson.core.jackson-databind" rev="2.0.2"
|
||||
conf="optional, jackson->compile"/>
|
||||
<dependency org="org.springframework" name="org.springframework.aop" rev="latest.integration"
|
||||
conf="compile->compile"/>
|
||||
<dependency org="org.springframework" name="org.springframework.beans" rev="latest.integration"
|
||||
|
||||
@@ -77,6 +77,39 @@
|
||||
</SOURCES>
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="module-library">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$IVY_CACHE$/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-core/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-core-2.0.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$IVY_CACHE$/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-core/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-core-sources-2.0.2.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="module-library">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$IVY_CACHE$/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-databind/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-databind-2.0.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$IVY_CACHE$/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-databind/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-databind-sources-2.0.2.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="module-library">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$IVY_CACHE$/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-annotations/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-annotations-2.0.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$IVY_CACHE$/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-annotations/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-annotations-sources-2.0.2.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</orderEntry>
|
||||
</component>
|
||||
<component name="copyright">
|
||||
<Base>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jms</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -736,7 +736,7 @@ public class JmsTemplate extends JmsDestinationAccessor implements JmsOperations
|
||||
JmsResourceHolder resourceHolder =
|
||||
(JmsResourceHolder) TransactionSynchronizationManager.getResource(getConnectionFactory());
|
||||
if (resourceHolder != null && resourceHolder.hasTimeout()) {
|
||||
timeout = resourceHolder.getTimeToLiveInMillis();
|
||||
timeout = Math.min(timeout, resourceHolder.getTimeToLiveInMillis());
|
||||
}
|
||||
Message message = doReceive(consumer, timeout);
|
||||
if (session.getTransacted()) {
|
||||
|
||||
+1
@@ -214,6 +214,7 @@ public abstract class AbstractJmsListeningContainer extends JmsDestinationAccess
|
||||
wasRunning = this.running;
|
||||
this.running = false;
|
||||
this.active = false;
|
||||
this.pausedTasks.clear();
|
||||
this.lifecycleMonitor.notifyAll();
|
||||
}
|
||||
|
||||
|
||||
+14
-16
@@ -297,9 +297,10 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
|
||||
* to scale the consumption of messages coming in from a queue. However,
|
||||
* note that any ordering guarantees are lost once multiple consumers are
|
||||
* registered. In general, stick with 1 consumer for low-volume queues.
|
||||
* <p><b>Do not raise the number of concurrent consumers for a topic.</b>
|
||||
* This would lead to concurrent consumption of the same message,
|
||||
* which is hardly ever desirable.
|
||||
* <p><b>Do not raise the number of concurrent consumers for a topic,
|
||||
* unless vendor-specific setup measures clearly allow for it.</b>
|
||||
* With regular setup, this would lead to concurrent consumption
|
||||
* of the same message, which is hardly ever desirable.
|
||||
* <p><b>This setting can be modified at runtime, for example through JMX.</b>
|
||||
* @see #setMaxConcurrentConsumers
|
||||
*/
|
||||
@@ -336,9 +337,10 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
|
||||
* to scale the consumption of messages coming in from a queue. However,
|
||||
* note that any ordering guarantees are lost once multiple consumers are
|
||||
* registered. In general, stick with 1 consumer for low-volume queues.
|
||||
* <p><b>Do not raise the number of concurrent consumers for a topic.</b>
|
||||
* This would lead to concurrent consumption of the same message,
|
||||
* which is hardly ever desirable.
|
||||
* <p><b>Do not raise the number of concurrent consumers for a topic,
|
||||
* unless vendor-specific setup measures clearly allow for it.</b>
|
||||
* With regular setup, this would lead to concurrent consumption
|
||||
* of the same message, which is hardly ever desirable.
|
||||
* <p><b>This setting can be modified at runtime, for example through JMX.</b>
|
||||
* @see #setConcurrentConsumers
|
||||
*/
|
||||
@@ -470,16 +472,6 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void validateConfiguration() {
|
||||
super.validateConfiguration();
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
if (isSubscriptionDurable() && this.concurrentConsumers != 1) {
|
||||
throw new IllegalArgumentException("Only 1 concurrent consumer supported for durable subscription");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Implementation of AbstractMessageListenerContainer's template methods
|
||||
@@ -535,6 +527,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
|
||||
logger.debug("Waiting for shutdown of message listener invokers");
|
||||
try {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
// Waiting for AsyncMessageListenerInvokers to deactivate themselves...
|
||||
while (this.activeInvokerCount > 0) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Still waiting for shutdown of " + this.activeInvokerCount +
|
||||
@@ -542,6 +535,11 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
|
||||
}
|
||||
this.lifecycleMonitor.wait();
|
||||
}
|
||||
// Clear remaining scheduled invokers, possibly left over as paused tasks...
|
||||
for (AsyncMessageListenerInvoker scheduledInvoker : this.scheduledInvokers) {
|
||||
scheduledInvoker.clearResources();
|
||||
}
|
||||
this.scheduledInvokers.clear();
|
||||
}
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
|
||||
+370
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* 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.springframework.jms.support.converter;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.jms.BytesMessage;
|
||||
import javax.jms.JMSException;
|
||||
import javax.jms.Message;
|
||||
import javax.jms.Session;
|
||||
import javax.jms.TextMessage;
|
||||
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Message converter that uses the Jackson 2 library to convert messages to and from JSON.
|
||||
* Maps an object to a {@link javax.jms.BytesMessage}, or to a {@link javax.jms.TextMessage} if the
|
||||
* {@link #setTargetType targetType} is set to {@link org.springframework.jms.support.converter.MessageType#TEXT}.
|
||||
* Converts from a {@link javax.jms.TextMessage} or {@link javax.jms.BytesMessage} to an object.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @author Dave Syer
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1.4
|
||||
*/
|
||||
public class MappingJackson2MessageConverter implements MessageConverter {
|
||||
|
||||
/**
|
||||
* The default encoding used for writing to text messages: UTF-8.
|
||||
*/
|
||||
public static final String DEFAULT_ENCODING = "UTF-8";
|
||||
|
||||
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private MessageType targetType = MessageType.BYTES;
|
||||
|
||||
private String encoding = DEFAULT_ENCODING;
|
||||
|
||||
private String encodingPropertyName;
|
||||
|
||||
private String typeIdPropertyName;
|
||||
|
||||
private Map<String, Class<?>> idClassMappings = new HashMap<String, Class<?>>();
|
||||
|
||||
private Map<Class<?>, String> classIdMappings = new HashMap<Class<?>, String>();
|
||||
|
||||
|
||||
/**
|
||||
* Specify the {@link org.codehaus.jackson.map.ObjectMapper} to use instead of using the default.
|
||||
*/
|
||||
public void setObjectMapper(ObjectMapper objectMapper) {
|
||||
Assert.notNull(objectMapper, "ObjectMapper must not be null");
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether {@link #toMessage(Object, javax.jms.Session)} should marshal to a
|
||||
* {@link javax.jms.BytesMessage} or a {@link javax.jms.TextMessage}.
|
||||
* <p>The default is {@link org.springframework.jms.support.converter.MessageType#BYTES}, i.e. this converter marshals to
|
||||
* a {@link javax.jms.BytesMessage}. Note that the default version of this converter
|
||||
* supports {@link org.springframework.jms.support.converter.MessageType#BYTES} and {@link org.springframework.jms.support.converter.MessageType#TEXT} only.
|
||||
* @see org.springframework.jms.support.converter.MessageType#BYTES
|
||||
* @see org.springframework.jms.support.converter.MessageType#TEXT
|
||||
*/
|
||||
public void setTargetType(MessageType targetType) {
|
||||
Assert.notNull(targetType, "MessageType must not be null");
|
||||
this.targetType = targetType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the encoding to use when converting to and from text-based
|
||||
* message body content. The default encoding will be "UTF-8".
|
||||
* <p>When reading from a a text-based message, an encoding may have been
|
||||
* suggested through a special JMS property which will then be preferred
|
||||
* over the encoding set on this MessageConverter instance.
|
||||
* @see #setEncodingPropertyName
|
||||
*/
|
||||
public void setEncoding(String encoding) {
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the name of the JMS message property that carries the encoding from
|
||||
* bytes to String and back is BytesMessage is used during the conversion process.
|
||||
* <p>Default is none. Setting this property is optional; if not set, UTF-8 will
|
||||
* be used for decoding any incoming bytes message.
|
||||
* @see #setEncoding
|
||||
*/
|
||||
public void setEncodingPropertyName(String encodingPropertyName) {
|
||||
this.encodingPropertyName = encodingPropertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the name of the JMS message property that carries the type id for the
|
||||
* contained object: either a mapped id value or a raw Java class name.
|
||||
* <p>Default is none. <b>NOTE: This property needs to be set in order to allow
|
||||
* for converting from an incoming message to a Java object.</b>
|
||||
* @see #setTypeIdMappings
|
||||
*/
|
||||
public void setTypeIdPropertyName(String typeIdPropertyName) {
|
||||
this.typeIdPropertyName = typeIdPropertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify mappings from type ids to Java classes, if desired.
|
||||
* This allows for synthetic ids in the type id message property,
|
||||
* instead of transferring Java class names.
|
||||
* <p>Default is no custom mappings, i.e. transferring raw Java class names.
|
||||
* @param typeIdMappings a Map with type id values as keys and Java classes as values
|
||||
*/
|
||||
public void setTypeIdMappings(Map<String, Class<?>> typeIdMappings) {
|
||||
this.idClassMappings = new HashMap<String, Class<?>>();
|
||||
for (Map.Entry<String, Class<?>> entry : typeIdMappings.entrySet()) {
|
||||
String id = entry.getKey();
|
||||
Class<?> clazz = entry.getValue();
|
||||
this.idClassMappings.put(id, clazz);
|
||||
this.classIdMappings.put(clazz, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Message toMessage(Object object, Session session) throws JMSException, MessageConversionException {
|
||||
Message message;
|
||||
try {
|
||||
switch (this.targetType) {
|
||||
case TEXT:
|
||||
message = mapToTextMessage(object, session, this.objectMapper);
|
||||
break;
|
||||
case BYTES:
|
||||
message = mapToBytesMessage(object, session, this.objectMapper);
|
||||
break;
|
||||
default:
|
||||
message = mapToMessage(object, session, this.objectMapper, this.targetType);
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new MessageConversionException("Could not map JSON object [" + object + "]", ex);
|
||||
}
|
||||
setTypeIdOnMessage(object, message);
|
||||
return message;
|
||||
}
|
||||
|
||||
public Object fromMessage(Message message) throws JMSException, MessageConversionException {
|
||||
try {
|
||||
JavaType targetJavaType = getJavaTypeForMessage(message);
|
||||
return convertToObject(message, targetJavaType);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new MessageConversionException("Failed to convert JSON message content", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Map the given object to a {@link javax.jms.TextMessage}.
|
||||
* @param object the object to be mapped
|
||||
* @param session current JMS session
|
||||
* @param objectMapper the mapper to use
|
||||
* @return the resulting message
|
||||
* @throws javax.jms.JMSException if thrown by JMS methods
|
||||
* @throws java.io.IOException in case of I/O errors
|
||||
* @see javax.jms.Session#createBytesMessage
|
||||
* @see org.springframework.oxm.Marshaller#marshal(Object, javax.xml.transform.Result)
|
||||
*/
|
||||
protected TextMessage mapToTextMessage(Object object, Session session, ObjectMapper objectMapper)
|
||||
throws JMSException, IOException {
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
objectMapper.writeValue(writer, object);
|
||||
return session.createTextMessage(writer.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the given object to a {@link javax.jms.BytesMessage}.
|
||||
* @param object the object to be mapped
|
||||
* @param session current JMS session
|
||||
* @param objectMapper the mapper to use
|
||||
* @return the resulting message
|
||||
* @throws javax.jms.JMSException if thrown by JMS methods
|
||||
* @throws java.io.IOException in case of I/O errors
|
||||
* @see javax.jms.Session#createBytesMessage
|
||||
* @see org.springframework.oxm.Marshaller#marshal(Object, javax.xml.transform.Result)
|
||||
*/
|
||||
protected BytesMessage mapToBytesMessage(Object object, Session session, ObjectMapper objectMapper)
|
||||
throws JMSException, IOException {
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
OutputStreamWriter writer = new OutputStreamWriter(bos, this.encoding);
|
||||
objectMapper.writeValue(writer, object);
|
||||
|
||||
BytesMessage message = session.createBytesMessage();
|
||||
message.writeBytes(bos.toByteArray());
|
||||
if (this.encodingPropertyName != null) {
|
||||
message.setStringProperty(this.encodingPropertyName, this.encoding);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that allows for custom message mapping.
|
||||
* Invoked when {@link #setTargetType} is not {@link org.springframework.jms.support.converter.MessageType#TEXT} or
|
||||
* {@link org.springframework.jms.support.converter.MessageType#BYTES}.
|
||||
* <p>The default implementation throws an {@link IllegalArgumentException}.
|
||||
* @param object the object to marshal
|
||||
* @param session the JMS Session
|
||||
* @param objectMapper the mapper to use
|
||||
* @param targetType the target message type (other than TEXT or BYTES)
|
||||
* @return the resulting message
|
||||
* @throws javax.jms.JMSException if thrown by JMS methods
|
||||
* @throws java.io.IOException in case of I/O errors
|
||||
*/
|
||||
protected Message mapToMessage(Object object, Session session, ObjectMapper objectMapper, MessageType targetType)
|
||||
throws JMSException, IOException {
|
||||
|
||||
throw new IllegalArgumentException("Unsupported message type [" + targetType +
|
||||
"]. MappingJacksonMessageConverter by default only supports TextMessages and BytesMessages.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a type id for the given payload object on the given JMS Message.
|
||||
* <p>The default implementation consults the configured type id mapping and
|
||||
* sets the resulting value (either a mapped id or the raw Java class name)
|
||||
* into the configured type id message property.
|
||||
* @param object the payload object to set a type id for
|
||||
* @param message the JMS Message to set the type id on
|
||||
* @throws javax.jms.JMSException if thrown by JMS methods
|
||||
* @see #getJavaTypeForMessage(javax.jms.Message)
|
||||
* @see #setTypeIdPropertyName(String)
|
||||
* @see #setTypeIdMappings(java.util.Map)
|
||||
*/
|
||||
protected void setTypeIdOnMessage(Object object, Message message) throws JMSException {
|
||||
if (this.typeIdPropertyName != null) {
|
||||
String typeId = this.classIdMappings.get(object.getClass());
|
||||
if (typeId == null) {
|
||||
typeId = object.getClass().getName();
|
||||
}
|
||||
message.setStringProperty(this.typeIdPropertyName, typeId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convenience method to dispatch to converters for individual message types.
|
||||
*/
|
||||
private Object convertToObject(Message message, JavaType targetJavaType) throws JMSException, IOException {
|
||||
if (message instanceof TextMessage) {
|
||||
return convertFromTextMessage((TextMessage) message, targetJavaType);
|
||||
}
|
||||
else if (message instanceof BytesMessage) {
|
||||
return convertFromBytesMessage((BytesMessage) message, targetJavaType);
|
||||
}
|
||||
else {
|
||||
return convertFromMessage(message, targetJavaType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a TextMessage to a Java Object with the specified type.
|
||||
* @param message the input message
|
||||
* @param targetJavaType the target type
|
||||
* @return the message converted to an object
|
||||
* @throws javax.jms.JMSException if thrown by JMS
|
||||
* @throws java.io.IOException in case of I/O errors
|
||||
*/
|
||||
protected Object convertFromTextMessage(TextMessage message, JavaType targetJavaType)
|
||||
throws JMSException, IOException {
|
||||
|
||||
String body = message.getText();
|
||||
return this.objectMapper.readValue(body, targetJavaType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a BytesMessage to a Java Object with the specified type.
|
||||
* @param message the input message
|
||||
* @param targetJavaType the target type
|
||||
* @return the message converted to an object
|
||||
* @throws javax.jms.JMSException if thrown by JMS
|
||||
* @throws java.io.IOException in case of I/O errors
|
||||
*/
|
||||
protected Object convertFromBytesMessage(BytesMessage message, JavaType targetJavaType)
|
||||
throws JMSException, IOException {
|
||||
|
||||
String encoding = this.encoding;
|
||||
if (this.encodingPropertyName != null && message.propertyExists(this.encodingPropertyName)) {
|
||||
encoding = message.getStringProperty(this.encodingPropertyName);
|
||||
}
|
||||
byte[] bytes = new byte[(int) message.getBodyLength()];
|
||||
message.readBytes(bytes);
|
||||
try {
|
||||
String body = new String(bytes, encoding);
|
||||
return this.objectMapper.readValue(body, targetJavaType);
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
throw new MessageConversionException("Cannot convert bytes to String", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that allows for custom message mapping.
|
||||
* Invoked when {@link #setTargetType} is not {@link org.springframework.jms.support.converter.MessageType#TEXT} or
|
||||
* {@link org.springframework.jms.support.converter.MessageType#BYTES}.
|
||||
* <p>The default implementation throws an {@link IllegalArgumentException}.
|
||||
* @param message the input message
|
||||
* @param targetJavaType the target type
|
||||
* @return the message converted to an object
|
||||
* @throws javax.jms.JMSException if thrown by JMS
|
||||
* @throws java.io.IOException in case of I/O errors
|
||||
*/
|
||||
protected Object convertFromMessage(Message message, JavaType targetJavaType)
|
||||
throws JMSException, IOException {
|
||||
|
||||
throw new IllegalArgumentException("Unsupported message type [" + message.getClass() +
|
||||
"]. MappingJacksonMessageConverter by default only supports TextMessages and BytesMessages.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a Jackson JavaType for the given JMS Message,
|
||||
* typically parsing a type id message property.
|
||||
* <p>The default implementation parses the configured type id property name
|
||||
* and consults the configured type id mapping. This can be overridden with
|
||||
* a different strategy, e.g. doing some heuristics based on message origin.
|
||||
* @param message the JMS Message to set the type id on
|
||||
* @throws javax.jms.JMSException if thrown by JMS methods
|
||||
* @see #setTypeIdOnMessage(Object, javax.jms.Message)
|
||||
* @see #setTypeIdPropertyName(String)
|
||||
* @see #setTypeIdMappings(java.util.Map)
|
||||
*/
|
||||
protected JavaType getJavaTypeForMessage(Message message) throws JMSException {
|
||||
String typeId = message.getStringProperty(this.typeIdPropertyName);
|
||||
if (typeId == null) {
|
||||
throw new MessageConversionException("Could not find type id property [" + this.typeIdPropertyName + "]");
|
||||
}
|
||||
Class mappedClass = this.idClassMappings.get(typeId);
|
||||
if (mappedClass != null) {
|
||||
return this.objectMapper.getTypeFactory().constructType(mappedClass);
|
||||
}
|
||||
try {
|
||||
return this.objectMapper.getTypeFactory().constructType(
|
||||
ClassUtils.forName(typeId, getClass().getClassLoader()));
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new MessageConversionException("Failed to resolve type id [" + typeId + "]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* 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.springframework.jms.support.converter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.jms.BytesMessage;
|
||||
import javax.jms.Session;
|
||||
import javax.jms.TextMessage;
|
||||
|
||||
import org.easymock.Capture;
|
||||
import org.easymock.EasyMock;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.easymock.EasyMock.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class MappingJackson2MessageConverterTests {
|
||||
|
||||
private MappingJackson2MessageConverter converter;
|
||||
|
||||
private Session sessionMock;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
sessionMock = createMock(Session.class);
|
||||
converter = new MappingJackson2MessageConverter();
|
||||
converter.setEncodingPropertyName("__encoding__");
|
||||
converter.setTypeIdPropertyName("__typeid__");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toBytesMessage() throws Exception {
|
||||
BytesMessage bytesMessageMock = createMock(BytesMessage.class);
|
||||
Date toBeMarshalled = new Date();
|
||||
|
||||
expect(sessionMock.createBytesMessage()).andReturn(bytesMessageMock);
|
||||
bytesMessageMock.setStringProperty("__encoding__", "UTF-8");
|
||||
bytesMessageMock.setStringProperty("__typeid__", Date.class.getName());
|
||||
bytesMessageMock.writeBytes(isA(byte[].class));
|
||||
|
||||
replay(sessionMock, bytesMessageMock);
|
||||
|
||||
converter.toMessage(toBeMarshalled, sessionMock);
|
||||
|
||||
verify(sessionMock, bytesMessageMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromBytesMessage() throws Exception {
|
||||
BytesMessage bytesMessageMock = createMock(BytesMessage.class);
|
||||
Map<String, String> unmarshalled = Collections.singletonMap("foo", "bar");
|
||||
|
||||
final byte[] bytes = "{\"foo\":\"bar\"}".getBytes();
|
||||
Capture<byte[]> captured = new Capture<byte[]>() {
|
||||
@Override
|
||||
public void setValue(byte[] value) {
|
||||
super.setValue(value);
|
||||
System.arraycopy(bytes, 0, value, 0, bytes.length);
|
||||
}
|
||||
};
|
||||
|
||||
expect(
|
||||
bytesMessageMock.getStringProperty("__typeid__"))
|
||||
.andReturn(Object.class.getName());
|
||||
expect(
|
||||
bytesMessageMock.propertyExists("__encoding__"))
|
||||
.andReturn(false);
|
||||
expect(bytesMessageMock.getBodyLength()).andReturn(
|
||||
new Long(bytes.length));
|
||||
expect(bytesMessageMock.readBytes(EasyMock.capture(captured)))
|
||||
.andReturn(bytes.length);
|
||||
|
||||
replay(sessionMock, bytesMessageMock);
|
||||
|
||||
Object result = converter.fromMessage(bytesMessageMock);
|
||||
assertEquals("Invalid result", result, unmarshalled);
|
||||
|
||||
verify(sessionMock, bytesMessageMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toTextMessageWithObject() throws Exception {
|
||||
converter.setTargetType(MessageType.TEXT);
|
||||
TextMessage textMessageMock = createMock(TextMessage.class);
|
||||
Date toBeMarshalled = new Date();
|
||||
|
||||
textMessageMock.setStringProperty("__typeid__", Date.class.getName());
|
||||
expect(sessionMock.createTextMessage(isA(String.class))).andReturn( textMessageMock);
|
||||
|
||||
replay(sessionMock, textMessageMock);
|
||||
|
||||
converter.toMessage(toBeMarshalled, sessionMock);
|
||||
|
||||
verify(sessionMock, textMessageMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toTextMessageWithMap() throws Exception {
|
||||
converter.setTargetType(MessageType.TEXT);
|
||||
TextMessage textMessageMock = createMock(TextMessage.class);
|
||||
Map<String, String> toBeMarshalled = new HashMap<String, String>();
|
||||
toBeMarshalled.put("foo", "bar");
|
||||
|
||||
textMessageMock.setStringProperty("__typeid__", HashMap.class.getName());
|
||||
expect(sessionMock.createTextMessage(isA(String.class))).andReturn(
|
||||
textMessageMock);
|
||||
|
||||
replay(sessionMock, textMessageMock);
|
||||
|
||||
converter.toMessage(toBeMarshalled, sessionMock);
|
||||
|
||||
verify(sessionMock, textMessageMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromTextMessageAsObject() throws Exception {
|
||||
TextMessage textMessageMock = createMock(TextMessage.class);
|
||||
Map<String, String> unmarshalled = Collections.singletonMap("foo", "bar");
|
||||
|
||||
String text = "{\"foo\":\"bar\"}";
|
||||
expect(
|
||||
textMessageMock.getStringProperty("__typeid__"))
|
||||
.andReturn(Object.class.getName());
|
||||
expect(textMessageMock.getText()).andReturn(text);
|
||||
|
||||
replay(sessionMock, textMessageMock);
|
||||
|
||||
Object result = converter.fromMessage(textMessageMock);
|
||||
assertEquals("Invalid result", result, unmarshalled);
|
||||
|
||||
verify(sessionMock, textMessageMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromTextMessageAsMap() throws Exception {
|
||||
TextMessage textMessageMock = createMock(TextMessage.class);
|
||||
Map<String, String> unmarshalled = Collections.singletonMap("foo", "bar");
|
||||
|
||||
String text = "{\"foo\":\"bar\"}";
|
||||
expect(
|
||||
textMessageMock.getStringProperty("__typeid__"))
|
||||
.andReturn(HashMap.class.getName());
|
||||
expect(textMessageMock.getText()).andReturn(text);
|
||||
|
||||
replay(sessionMock, textMessageMock);
|
||||
|
||||
Object result = converter.fromMessage(textMessageMock);
|
||||
assertEquals("Invalid result", result, unmarshalled);
|
||||
|
||||
verify(sessionMock, textMessageMock);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@ Bundle-Name: Spring JMS
|
||||
Bundle-Vendor: SpringSource
|
||||
Bundle-ManifestVersion: 2
|
||||
Import-Template:
|
||||
com.fasterxml.jackson.*;version="[2.0.0, 3.0.0)";resolution:=optional,
|
||||
javax.jms.*;version="[1.1.0, 2.0.0)",
|
||||
javax.naming.*;version="0";resolution:=optional,
|
||||
javax.resource.*;version="[1.5.0, 2.0.0)";resolution:=optional,
|
||||
|
||||
@@ -40,9 +40,9 @@
|
||||
<dependency org="org.apache.openjpa" name="com.springsource.org.apache.openjpa" rev="1.1.0" conf="optional, openjpa->compile"/>
|
||||
<dependency org="org.eclipse.persistence" name="com.springsource.org.eclipse.persistence" rev="1.0.1" conf="test->compile"/>
|
||||
<dependency org="org.eclipse.persistence" name="com.springsource.org.eclipse.persistence.jpa" rev="1.0.1" conf="optional, eclipselink->compile"/>
|
||||
<dependency org="org.hibernate" name="com.springsource.org.hibernate" rev="3.3.1.GA" conf="optional, hibernate->compile"/>
|
||||
<dependency org="org.hibernate" name="com.springsource.org.hibernate.annotations" rev="3.4.0.GA" conf="optional, hibernate-jpa->compile"/>
|
||||
<dependency org="org.hibernate" name="com.springsource.org.hibernate.ejb" rev="3.4.0.GA" conf="optional, hibernate-jpa->compile"/>
|
||||
<dependency org="org.hibernate" name="com.springsource.org.hibernate" rev="3.3.2.GA" conf="optional, hibernate->compile"/>
|
||||
<dependency org="org.hibernate" name="com.springsource.org.hibernate.annotations" rev="3.4.0.GA-A" conf="optional, hibernate-jpa->compile"/>
|
||||
<dependency org="org.hibernate" name="com.springsource.org.hibernate.ejb" rev="3.4.0.GA-A" conf="optional, hibernate-jpa->compile"/>
|
||||
<dependency org="org.hibernate" name="com.springsource.org.hibernate.core" rev="4.1.0.Final" conf="optional, hibernate->compile"/>
|
||||
<dependency org="javax.jdo" name="com.springsource.javax.jdo" rev="2.1.0" conf="provided, jdo->compile"/>
|
||||
<dependency org="javax.persistence" name="com.springsource.javax.persistence" rev="1.0.0" conf="provided, jpa->compile"/>
|
||||
@@ -62,7 +62,7 @@
|
||||
<dependency org="org.easymock" name="com.springsource.org.easymock" rev="2.5.1" conf="test->compile"/>
|
||||
<dependency org="org.hsqldb" name="com.springsource.org.hsqldb" rev="1.8.0.9" conf="test->compile"/>
|
||||
<dependency org="org.junit" name="com.springsource.org.junit" rev="${junit.version}" conf="test->runtime"/>
|
||||
<dependency org="org.slf4j" name="com.springsource.slf4j.jcl" rev="1.5.3" conf="test->runtime"/>
|
||||
<dependency org="org.slf4j" name="com.springsource.slf4j.jcl" rev="1.6.1" conf="test->runtime"/>
|
||||
</dependencies>
|
||||
|
||||
</ivy-module>
|
||||
|
||||
@@ -177,11 +177,11 @@
|
||||
<orderEntry type="module-library">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$IVY_CACHE$/org.hibernate/com.springsource.org.hibernate/3.3.1.GA/com.springsource.org.hibernate-3.3.1.GA.jar!/" />
|
||||
<root url="jar://$IVY_CACHE$/org.hibernate/com.springsource.org.hibernate/3.3.2.GA/com.springsource.org.hibernate-3.3.2.GA.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$IVY_CACHE$/org.hibernate/com.springsource.org.hibernate/3.3.1.GA/com.springsource.org.hibernate-sources-3.3.1.GA.jar!/" />
|
||||
<root url="jar://$IVY_CACHE$/org.hibernate/com.springsource.org.hibernate/3.3.2.GA/com.springsource.org.hibernate-sources-3.3.2.GA.jar!/java" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</orderEntry>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-orm</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+3
@@ -30,7 +30,10 @@ import org.hibernate.cache.CacheProvider;
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.1
|
||||
* @see LocalSessionFactoryBean#setCacheProvider
|
||||
* @deprecated as of Spring 3.0, following Hibernate 3.3's deprecation
|
||||
* of the CacheProvider SPI
|
||||
*/
|
||||
@Deprecated
|
||||
public class LocalCacheProviderProxy implements CacheProvider {
|
||||
|
||||
private final CacheProvider cacheProvider;
|
||||
|
||||
+5
-3
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Package providing integration of
|
||||
@@ -7,10 +6,13 @@
|
||||
*
|
||||
* <p>Contains an implementation of Spring's transaction SPI for local Hibernate transactions.
|
||||
* This package is intentionally rather minimal, with no template classes or the like,
|
||||
* in order to follow native Hibernate recommendations as closely as possible.
|
||||
* in order to follow Hibernate recommendations as closely as possible. We recommend
|
||||
* using Hibernate's native <code>sessionFactory.getCurrentSession()</code> style.
|
||||
*
|
||||
* <p><b>This package supports Hibernate 4.x only.</b>
|
||||
* See the <code>org.springframework.orm.hibernate3</code> package for Hibernate 3.x support.
|
||||
* See the {@code org.springframework.orm.hibernate3} package for Hibernate 3.x support.
|
||||
* <b>Note:</b> Do not use HibernateTemplate or other classes from the hibernate3 package
|
||||
* with Hibernate 4; this will lead to class definition exceptions at runtime.
|
||||
*
|
||||
*/
|
||||
package org.springframework.orm.hibernate4;
|
||||
|
||||
+28
-33
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -76,12 +76,14 @@ public abstract class EntityManagerFactoryUtils {
|
||||
* Find an EntityManagerFactory with the given name in the given
|
||||
* Spring application context (represented as ListableBeanFactory).
|
||||
* <p>The specified unit name will be matched against the configured
|
||||
* peristence unit, provided that a discovered EntityManagerFactory
|
||||
* persistence unit, provided that a discovered EntityManagerFactory
|
||||
* implements the {@link EntityManagerFactoryInfo} interface. If not,
|
||||
* the persistence unit name will be matched against the Spring bean name,
|
||||
* assuming that the EntityManagerFactory bean names follow that convention.
|
||||
* <p>If no unit name has been given, this method will search for a default
|
||||
* EntityManagerFactory through {@link ListableBeanFactory#getBean(Class)}.
|
||||
* @param beanFactory the ListableBeanFactory to search
|
||||
* @param unitName the name of the persistence unit (may be <code>null</code> or empty,
|
||||
* @param unitName the name of the persistence unit (may be {@code null} or empty,
|
||||
* in which case a single bean of type EntityManagerFactory will be searched for)
|
||||
* @return the EntityManagerFactory
|
||||
* @throws NoSuchBeanDefinitionException if there is no such EntityManagerFactory in the context
|
||||
@@ -108,7 +110,8 @@ public abstract class EntityManagerFactoryUtils {
|
||||
return beanFactory.getBean(unitName, EntityManagerFactory.class);
|
||||
}
|
||||
else {
|
||||
return BeanFactoryUtils.beanOfType(beanFactory, EntityManagerFactory.class);
|
||||
// Find unique EntityManagerFactory bean in the context, falling back to parent contexts.
|
||||
return beanFactory.getBean(EntityManagerFactory.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,9 +119,9 @@ public abstract class EntityManagerFactoryUtils {
|
||||
* Obtain a JPA EntityManager from the given factory. Is aware of a
|
||||
* corresponding EntityManager bound to the current thread,
|
||||
* for example when using JpaTransactionManager.
|
||||
* <p>Note: Will return <code>null</code> if no thread-bound EntityManager found!
|
||||
* <p>Note: Will return {@code null} if no thread-bound EntityManager found!
|
||||
* @param emf EntityManagerFactory to create the EntityManager with
|
||||
* @return the EntityManager, or <code>null</code> if none found
|
||||
* @return the EntityManager, or {@code null} if none found
|
||||
* @throws DataAccessResourceFailureException if the EntityManager couldn't be obtained
|
||||
* @see JpaTransactionManager
|
||||
*/
|
||||
@@ -132,11 +135,11 @@ public abstract class EntityManagerFactoryUtils {
|
||||
* Obtain a JPA EntityManager from the given factory. Is aware of a
|
||||
* corresponding EntityManager bound to the current thread,
|
||||
* for example when using JpaTransactionManager.
|
||||
* <p>Note: Will return <code>null</code> if no thread-bound EntityManager found!
|
||||
* <p>Note: Will return {@code null} if no thread-bound EntityManager found!
|
||||
* @param emf EntityManagerFactory to create the EntityManager with
|
||||
* @param properties the properties to be passed into the <code>createEntityManager</code>
|
||||
* call (may be <code>null</code>)
|
||||
* @return the EntityManager, or <code>null</code> if none found
|
||||
* @param properties the properties to be passed into the {@code createEntityManager}
|
||||
* call (may be {@code null})
|
||||
* @return the EntityManager, or {@code null} if none found
|
||||
* @throws DataAccessResourceFailureException if the EntityManager couldn't be obtained
|
||||
* @see JpaTransactionManager
|
||||
*/
|
||||
@@ -154,11 +157,11 @@ public abstract class EntityManagerFactoryUtils {
|
||||
* Obtain a JPA EntityManager from the given factory. Is aware of a
|
||||
* corresponding EntityManager bound to the current thread,
|
||||
* for example when using JpaTransactionManager.
|
||||
* <p>Same as <code>getEntityManager</code>, but throwing the original PersistenceException.
|
||||
* <p>Same as {@code getEntityManager}, but throwing the original PersistenceException.
|
||||
* @param emf EntityManagerFactory to create the EntityManager with
|
||||
* @param properties the properties to be passed into the <code>createEntityManager</code>
|
||||
* call (may be <code>null</code>)
|
||||
* @return the EntityManager, or <code>null</code> if none found
|
||||
* @param properties the properties to be passed into the {@code createEntityManager}
|
||||
* call (may be {@code null})
|
||||
* @return the EntityManager, or {@code null} if none found
|
||||
* @throws javax.persistence.PersistenceException if the EntityManager couldn't be created
|
||||
* @see #getTransactionalEntityManager(javax.persistence.EntityManagerFactory)
|
||||
* @see JpaTransactionManager
|
||||
@@ -254,8 +257,7 @@ public abstract class EntityManagerFactoryUtils {
|
||||
|
||||
/**
|
||||
* Apply the current transaction timeout, if any, to the given JPA Query object.
|
||||
* <p>This method sets the JPA 2.0 query hints "javax.persistence.lock.timeout"
|
||||
* and "javax.persistence.query.timeout" accordingly.
|
||||
* <p>This method sets the JPA 2.0 query hint "javax.persistence.query.timeout" accordingly.
|
||||
* @param query the JPA Query object
|
||||
* @param emf JPA EntityManagerFactory that the Query was created for
|
||||
*/
|
||||
@@ -263,33 +265,26 @@ public abstract class EntityManagerFactoryUtils {
|
||||
EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager.getResource(emf);
|
||||
if (emHolder != null && emHolder.hasTimeout()) {
|
||||
int timeoutValue = (int) emHolder.getTimeToLiveInMillis();
|
||||
try {
|
||||
query.setHint("javax.persistence.lock.timeout", timeoutValue);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
// oh well, at least we tried...
|
||||
}
|
||||
try {
|
||||
query.setHint("javax.persistence.query.timeout", timeoutValue);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
// once again, at least we tried...
|
||||
// oh well, at least we tried...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given runtime exception to an appropriate exception from the
|
||||
* <code>org.springframework.dao</code> hierarchy.
|
||||
* {@code org.springframework.dao} hierarchy.
|
||||
* Return null if no translation is appropriate: any other exception may
|
||||
* have resulted from user code, and should not be translated.
|
||||
* <p>The most important cases like object not found or optimistic locking
|
||||
* failure are covered here. For more fine-granular conversion, JpaAccessor and
|
||||
* JpaTransactionManager support sophisticated translation of exceptions via a
|
||||
* JpaDialect.
|
||||
* @param ex runtime exception that occured
|
||||
* <p>The most important cases like object not found or optimistic locking failure
|
||||
* are covered here. For more fine-granular conversion, JpaTransactionManager etc
|
||||
* support sophisticated translation of exceptions via a JpaDialect.
|
||||
* @param ex runtime exception that occurred
|
||||
* @return the corresponding DataAccessException instance,
|
||||
* or <code>null</code> if the exception should not be translated
|
||||
* or {@code null} if the exception should not be translated
|
||||
*/
|
||||
public static DataAccessException convertJpaAccessExceptionIfPossible(RuntimeException ex) {
|
||||
// Following the JPA specification, a persistence provider can also
|
||||
@@ -325,17 +320,17 @@ public abstract class EntityManagerFactoryUtils {
|
||||
if (ex instanceof PersistenceException) {
|
||||
return new JpaSystemException((PersistenceException) ex);
|
||||
}
|
||||
|
||||
|
||||
// If we get here, we have an exception that resulted from user code,
|
||||
// rather than the persistence provider, so we return null to indicate
|
||||
// that translation should not occur.
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the given JPA EntityManager,
|
||||
* catching and logging any cleanup exceptions thrown.
|
||||
* @param em the JPA EntityManager to close (may be <code>null</code>)
|
||||
* @param em the JPA EntityManager to close (may be {@code null})
|
||||
* @see javax.persistence.EntityManager#close()
|
||||
*/
|
||||
public static void closeEntityManager(EntityManager em) {
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-oxm</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Package providing integration of <a href="http://www.castor.org/xml-mapping.html">Castor</a> within Springs O/X Mapping
|
||||
* Package providing integration of <a href="http://castor.codehaus.org/xml-mapping.html">Castor</a> within Springs O/X Mapping
|
||||
* support
|
||||
*
|
||||
*/
|
||||
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<xsd:schema xmlns="http://www.springframework.org/schema/oxm" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:tool="http://www.springframework.org/schema/tool"
|
||||
targetNamespace="http://www.springframework.org/schema/oxm" elementFormDefault="qualified"
|
||||
attributeFormDefault="unqualified">
|
||||
|
||||
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
|
||||
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines the elements used in Spring's Object/XML Mapping integration.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:element name="jaxb1-marshaller">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.oxm.jaxb.Jaxb1Marshaller">
|
||||
Defines a JAXB1 Marshaller.
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="org.springframework.oxm.jaxb.Jaxb1Marshaller"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:identifiedType">
|
||||
<xsd:attribute name="contextPath" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>The JAXB Context path</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="validating" type="xsd:boolean" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>Whether incoming XML should be validated.</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="jaxb2-marshaller">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.oxm.jaxb.Jaxb2Marshaller">
|
||||
Defines a JAXB2 Marshaller.
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="org.springframework.oxm.jaxb.Jaxb2Marshaller"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:identifiedType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="class-to-be-bound" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="name" type="classType" use="required"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="contextPath" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>The JAXB Context path</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="jibx-marshaller">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.oxm.jibx.JibxMarshaller">
|
||||
Defines a JiBX Marshaller.
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="org.springframework.oxm.jibx.JibxMarshaller"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:identifiedType">
|
||||
<xsd:attribute name="target-class" type="classType" use="required"/>
|
||||
<xsd:attribute name="bindingName" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>The binding name used by this marshaller.</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="xmlbeans-marshaller">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.oxm.xmlbeans.XmlBeansMarshaller">
|
||||
Defines a XMLBeans Marshaller.
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="org.springframework.oxm.xmlbeans.XmlBeansMarshaller"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:identifiedType">
|
||||
<xsd:attribute name="options" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.apache.xmlbeans.XmlOptions">
|
||||
The bean name of the XmlOptions that is to be used for this marshaller. Typically a
|
||||
XmlOptionsFactoryBean definition.
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.apache.xmlbeans.XmlOptions"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:simpleType name="classType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:java.lang.Class">A class supported by a marshaller.</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="direct">
|
||||
<tool:expected-type type="java.lang.Class"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:union memberTypes="xsd:string"/>
|
||||
</xsd:simpleType>
|
||||
|
||||
</xsd:schema>
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" "http://castor.org/mapping.dtd">
|
||||
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" "http://castor.codehaus.org/mapping.dtd">
|
||||
<mapping>
|
||||
<description>Castor generated mapping file</description>
|
||||
<class name="org.springframework.oxm.castor.Flights">
|
||||
@@ -28,4 +28,4 @@
|
||||
xmlns:tns="http://samples.springframework.org/flight"/>
|
||||
</field>
|
||||
</class>
|
||||
</mapping>
|
||||
</mapping>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-library</artifactId>
|
||||
<packaging>libd</packaging>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<name>Spring Framework</name>
|
||||
<description>Spring is a layered Java/J2EE application platform, based on code published in Expert
|
||||
One-on-One J2EE Design and Development by Rod Johnson (Wrox, 2002). </description>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>Spring Framework - Parent</name>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<description>Spring Framework Parent</description>
|
||||
<scm>
|
||||
<url>https://fisheye.springframework.org/browse/spring-framework</url>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user