Merge branch '3.2.x' into master

Conflicts:
	gradle.properties
	spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java
	spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java
	spring-core/src/main/java/org/springframework/core/convert/support/StringToEnumConverterFactory.java
	spring-core/src/main/java/org/springframework/core/env/ReadOnlySystemAttributesMap.java
	spring-jdbc/src/main/java/org/springframework/jdbc/datasource/LazyConnectionDataSourceProxy.java
	spring-jdbc/src/main/java/org/springframework/jdbc/support/lob/AbstractLobHandler.java
	spring-web/src/main/java/org/springframework/http/client/BufferingClientHttpRequestWrapper.java
	spring-web/src/main/java/org/springframework/http/client/SimpleBufferingClientHttpRequest.java
	spring-web/src/main/java/org/springframework/http/converter/BufferedImageHttpMessageConverter.java
	spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java
This commit is contained in:
Chris Beams
2013-03-04 15:41:15 +01:00
1450 changed files with 17678 additions and 42998 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 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.
@@ -32,6 +32,7 @@ import org.springframework.cache.Cache;
* always causes the method to be invoked and its result to be placed into the cache.
*
* @author Costin Leau
* @author Phillip Webb
* @since 3.1
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@@ -58,4 +59,13 @@ public @interface CachePut {
* <p>Default is "", meaning the method result is always cached.
*/
String condition() default "";
/**
* Spring Expression Language (SpEL) attribute used to veto the cache update.
* <p>Unlike {@link #condition()}, this expression is evaluated after the method
* has been called and can therefore refer to the {@code result}. Default is "",
* meaning that caching is never vetoed.
* @since 3.2
*/
String unless() default "";
}

View File

@@ -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.
@@ -30,6 +30,7 @@ import java.lang.annotation.Target;
* returned instance is used as the cache value.
*
* @author Costin Leau
* @author Phillip Webb
* @since 3.1
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@@ -56,4 +57,13 @@ public @interface Cacheable {
* <p>Default is "", meaning the method is always cached.
*/
String condition() default "";
/**
* Spring Expression Language (SpEL) attribute used to veto method caching.
* <p>Unlike {@link #condition()}, this expression is evaluated after the method
* has been called and can therefore refer to the {@code result}. Default is "",
* meaning that caching is never vetoed.
* @since 3.2
*/
String unless() default "";
}

View File

@@ -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.
@@ -16,12 +16,7 @@
package org.springframework.cache.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.*;
/**
* Group annotation for multiple cache annotations (of different or the same type).
@@ -30,7 +25,7 @@ import java.lang.annotation.Target;
* @author Chris Beams
* @since 3.1
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@@ -41,4 +36,5 @@ public @interface Caching {
CachePut[] put() default {};
CacheEvict[] evict() default {};
}

View File

@@ -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.
@@ -35,6 +35,7 @@ import org.springframework.util.ObjectUtils;
* @author Costin Leau
* @author Juergen Hoeller
* @author Chris Beams
* @author Phillip Webb
* @since 3.1
*/
@SuppressWarnings("serial")
@@ -82,6 +83,7 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
CacheableOperation cuo = new CacheableOperation();
cuo.setCacheNames(caching.value());
cuo.setCondition(caching.condition());
cuo.setUnless(caching.unless());
cuo.setKey(caching.key());
cuo.setName(ae.toString());
return cuo;
@@ -102,6 +104,7 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
CachePutOperation cuo = new CachePutOperation();
cuo.setCacheNames(caching.value());
cuo.setCondition(caching.condition());
cuo.setUnless(caching.unless());
cuo.setKey(caching.key());
cuo.setName(ae.toString());
return cuo;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 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.
@@ -44,6 +44,7 @@ import org.w3c.dom.Element;
* BeanDefinitionParser} for the {@code <tx:advice/>} tag.
*
* @author Costin Leau
* @author Phillip Webb
*/
class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
@@ -54,7 +55,9 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
*/
private static class Props {
private String key, condition, method;
private String key;
private String condition;
private String method;
private String[] caches = null;
Props(Element root) {
@@ -70,13 +73,9 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
<T extends CacheOperation> T merge(Element element, ReaderContext readerCtx, T op) {
String cache = element.getAttribute("cache");
String k = element.getAttribute("key");
String c = element.getAttribute("condition");
String[] localCaches = caches;
String localKey = key, localCondition = condition;
// sanity check
String[] localCaches = caches;
if (StringUtils.hasText(cache)) {
localCaches = StringUtils.commaDelimitedListToStringArray(cache.trim());
} else {
@@ -84,17 +83,10 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
readerCtx.error("No cache specified specified for " + element.getNodeName(), element);
}
}
if (StringUtils.hasText(k)) {
localKey = k.trim();
}
if (StringUtils.hasText(c)) {
localCondition = c.trim();
}
op.setCacheNames(localCaches);
op.setKey(localKey);
op.setCondition(localCondition);
op.setKey(getAttributeValue(element, "key", this.key));
op.setCondition(getAttributeValue(element, "condition", this.condition));
return op;
}
@@ -165,7 +157,8 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
String name = prop.merge(opElement, parserContext.getReaderContext());
TypedStringValue nameHolder = new TypedStringValue(name);
nameHolder.setSource(parserContext.extractSource(opElement));
CacheOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CacheableOperation());
CacheableOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CacheableOperation());
op.setUnless(getAttributeValue(opElement, "unless", ""));
Collection<CacheOperation> col = cacheOpMap.get(nameHolder);
if (col == null) {
@@ -207,7 +200,8 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
String name = prop.merge(opElement, parserContext.getReaderContext());
TypedStringValue nameHolder = new TypedStringValue(name);
nameHolder.setSource(parserContext.extractSource(opElement));
CacheOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CachePutOperation());
CachePutOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CachePutOperation());
op.setUnless(getAttributeValue(opElement, "unless", ""));
Collection<CacheOperation> col = cacheOpMap.get(nameHolder);
if (col == null) {
@@ -222,4 +216,14 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
attributeSourceDefinition.getPropertyValues().add("nameMap", cacheOpMap);
return attributeSourceDefinition;
}
private static String getAttributeValue(Element element, String attributeName, String defaultValue) {
String attribute = element.getAttribute(attributeName);
if(StringUtils.hasText(attribute)) {
return attribute.trim();
}
return defaultValue;
}
}

View File

@@ -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.
@@ -56,6 +56,7 @@ import org.springframework.util.StringUtils;
* @author Costin Leau
* @author Juergen Hoeller
* @author Chris Beams
* @author Phillip Webb
* @since 3.1
*/
public abstract class CacheAspectSupport implements InitializingBean {
@@ -212,7 +213,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
retVal = invoker.invoke();
inspectAfterCacheEvicts(ops.get(EVICT));
inspectAfterCacheEvicts(ops.get(EVICT), retVal);
if (!updates.isEmpty()) {
update(updates, retVal);
@@ -225,14 +226,16 @@ public abstract class CacheAspectSupport implements InitializingBean {
}
private void inspectBeforeCacheEvicts(Collection<CacheOperationContext> evictions) {
inspectCacheEvicts(evictions, true);
inspectCacheEvicts(evictions, true, ExpressionEvaluator.NO_RESULT);
}
private void inspectAfterCacheEvicts(Collection<CacheOperationContext> evictions) {
inspectCacheEvicts(evictions, false);
private void inspectAfterCacheEvicts(Collection<CacheOperationContext> evictions,
Object result) {
inspectCacheEvicts(evictions, false, result);
}
private void inspectCacheEvicts(Collection<CacheOperationContext> evictions, boolean beforeInvocation) {
private void inspectCacheEvicts(Collection<CacheOperationContext> evictions,
boolean beforeInvocation, Object result) {
if (!evictions.isEmpty()) {
@@ -242,7 +245,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
CacheEvictOperation evictOp = (CacheEvictOperation) context.operation;
if (beforeInvocation == evictOp.isBeforeInvocation()) {
if (context.isConditionPassing()) {
if (context.isConditionPassing(result)) {
// for each cache
// lazy key initialization
Object key = null;
@@ -278,7 +281,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
private CacheStatus inspectCacheables(Collection<CacheOperationContext> cacheables) {
Map<CacheOperationContext, Object> cUpdates = new LinkedHashMap<CacheOperationContext, Object>(cacheables.size());
boolean updateRequire = false;
boolean updateRequired = false;
Object retVal = null;
if (!cacheables.isEmpty()) {
@@ -305,7 +308,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
boolean localCacheHit = false;
// check whether the cache needs to be inspected or not (the method will be invoked anyway)
if (!updateRequire) {
if (!updateRequired) {
for (Cache cache : context.getCaches()) {
Cache.ValueWrapper wrapper = cache.get(key);
if (wrapper != null) {
@@ -317,7 +320,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
}
if (!localCacheHit) {
updateRequire = true;
updateRequired = true;
}
}
else {
@@ -329,7 +332,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
// return a status only if at least on cacheable matched
if (atLeastOnePassed) {
return new CacheStatus(cUpdates, updateRequire, retVal);
return new CacheStatus(cUpdates, updateRequired, retVal);
}
}
@@ -386,8 +389,11 @@ public abstract class CacheAspectSupport implements InitializingBean {
private void update(Map<CacheOperationContext, Object> updates, Object retVal) {
for (Map.Entry<CacheOperationContext, Object> entry : updates.entrySet()) {
for (Cache cache : entry.getKey().getCaches()) {
cache.put(entry.getValue(), retVal);
CacheOperationContext operationContext = entry.getKey();
if(operationContext.canPutToCache(retVal)) {
for (Cache cache : operationContext.getCaches()) {
cache.put(entry.getValue(), retVal);
}
}
}
}
@@ -427,30 +433,49 @@ public abstract class CacheAspectSupport implements InitializingBean {
private final CacheOperation operation;
private final Collection<Cache> caches;
private final Object target;
private final Method method;
private final Object[] args;
// context passed around to avoid multiple creations
private final EvaluationContext evalContext;
private final Object target;
private final Class<?> targetClass;
private final Collection<Cache> caches;
public CacheOperationContext(CacheOperation operation, Method method, Object[] args, Object target, Class<?> targetClass) {
this.operation = operation;
this.caches = CacheAspectSupport.this.getCaches(operation);
this.target = target;
this.method = method;
this.args = args;
this.evalContext = evaluator.createEvaluationContext(caches, method, args, target, targetClass);
this.target = target;
this.targetClass = targetClass;
this.caches = CacheAspectSupport.this.getCaches(operation);
}
protected boolean isConditionPassing() {
return isConditionPassing(ExpressionEvaluator.NO_RESULT);
}
protected boolean isConditionPassing(Object result) {
if (StringUtils.hasText(this.operation.getCondition())) {
return evaluator.condition(this.operation.getCondition(), this.method, this.evalContext);
EvaluationContext evaluationContext = createEvaluationContext(result);
return evaluator.condition(this.operation.getCondition(), this.method,
evaluationContext);
}
return true;
}
protected boolean canPutToCache(Object value) {
String unless = "";
if (this.operation instanceof CacheableOperation) {
unless = ((CacheableOperation) this.operation).getUnless();
}
else if (this.operation instanceof CachePutOperation) {
unless = ((CachePutOperation) this.operation).getUnless();
}
if(StringUtils.hasText(unless)) {
EvaluationContext evaluationContext = createEvaluationContext(value);
return !evaluator.unless(unless, this.method, evaluationContext);
}
return true;
}
@@ -461,13 +486,19 @@ public abstract class CacheAspectSupport implements InitializingBean {
*/
protected Object generateKey() {
if (StringUtils.hasText(this.operation.getKey())) {
return evaluator.key(this.operation.getKey(), this.method, this.evalContext);
EvaluationContext evaluationContext = createEvaluationContext(ExpressionEvaluator.NO_RESULT);
return evaluator.key(this.operation.getKey(), this.method, evaluationContext);
}
return keyGenerator.generate(this.target, this.method, this.args);
}
private EvaluationContext createEvaluationContext(Object result) {
return evaluator.createEvaluationContext(this.caches, this.method, this.args,
this.target, this.targetClass, result);
}
protected Collection<Cache> getCaches() {
return this.caches;
}
}
}
}

View File

@@ -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.
@@ -27,6 +27,7 @@ public class CacheEvictOperation extends CacheOperation {
private boolean cacheWide = false;
private boolean beforeInvocation = false;
public void setCacheWide(boolean cacheWide) {
this.cacheWide = cacheWide;
}

View File

@@ -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.
@@ -119,10 +119,10 @@ public abstract class CacheOperation {
result.append(this.name);
result.append("] caches=");
result.append(this.cacheNames);
result.append(" | condition='");
result.append(this.condition);
result.append("' | key='");
result.append(" | key='");
result.append(this.key);
result.append("' | condition='");
result.append(this.condition);
result.append("'");
return result;
}

View File

@@ -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.
@@ -20,8 +20,28 @@ package org.springframework.cache.interceptor;
* Class describing a cache 'put' operation.
*
* @author Costin Leau
* @author Phillip Webb
* @since 3.1
*/
public class CachePutOperation extends CacheOperation {
private String unless;
public String getUnless() {
return unless;
}
public void setUnless(String unless) {
this.unless = unless;
}
@Override
protected StringBuilder getOperationDescription() {
StringBuilder sb = super.getOperationDescription();
sb.append(" | unless='");
sb.append(this.unless);
sb.append("'");
return sb;
}
}

View File

@@ -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.
@@ -20,8 +20,28 @@ package org.springframework.cache.interceptor;
* Class describing a cache 'cacheable' operation.
*
* @author Costin Leau
* @author Phillip Webb
* @since 3.1
*/
public class CacheableOperation extends CacheOperation {
private String unless;
public String getUnless() {
return unless;
}
public void setUnless(String unless) {
this.unless = unless;
}
@Override
protected StringBuilder getOperationDescription() {
StringBuilder sb = super.getOperationDescription();
sb.append(" | unless='");
sb.append(this.unless);
sb.append("'");
return sb;
}
}

View File

@@ -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.
@@ -35,49 +35,84 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
* <p>Performs internal caching for performance reasons.
*
* @author Costin Leau
* @author Phillip Webb
* @since 3.1
*/
class ExpressionEvaluator {
public static final Object NO_RESULT = new Object();
private final SpelExpressionParser parser = new SpelExpressionParser();
// shared param discoverer since it caches data internally
private final ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
private final Map<String, Expression> keyCache = new ConcurrentHashMap<String, Expression>(64);
private final Map<String, Expression> conditionCache = new ConcurrentHashMap<String, Expression>(64);
private final Map<String, Expression> keyCache = new ConcurrentHashMap<String, Expression>(64);
private final Map<String, Expression> unlessCache = new ConcurrentHashMap<String, Expression>(64);
private final Map<String, Method> targetMethodCache = new ConcurrentHashMap<String, Method>(64);
public EvaluationContext createEvaluationContext(
Collection<Cache> caches, Method method, Object[] args, Object target, Class<?> targetClass) {
CacheExpressionRootObject rootObject =
new CacheExpressionRootObject(caches, method, args, target, targetClass);
return new LazyParamAwareEvaluationContext(rootObject,
this.paramNameDiscoverer, method, args, targetClass, this.targetMethodCache);
/**
* Create an {@link EvaluationContext} without a return value.
* @see #createEvaluationContext(Collection, Method, Object[], Object, Class, Object)
*/
public EvaluationContext createEvaluationContext(Collection<Cache> caches,
Method method, Object[] args, Object target, Class<?> targetClass) {
return createEvaluationContext(caches, method, args, target, targetClass,
NO_RESULT);
}
public boolean condition(String conditionExpression, Method method, EvaluationContext evalContext) {
String key = toString(method, conditionExpression);
Expression condExp = this.conditionCache.get(key);
if (condExp == null) {
condExp = this.parser.parseExpression(conditionExpression);
this.conditionCache.put(key, condExp);
/**
* Create an {@link EvaluationContext}.
*
* @param caches the current caches
* @param method the method
* @param args the method arguments
* @param target the target object
* @param targetClass the target class
* @param result the return value (can be {@code null}) or
* {@link #NO_RESULT} if there is no return at this time
* @return the evalulation context
*/
public EvaluationContext createEvaluationContext(Collection<Cache> caches,
Method method, Object[] args, Object target, Class<?> targetClass,
final Object result) {
CacheExpressionRootObject rootObject = new CacheExpressionRootObject(caches,
method, args, target, targetClass);
LazyParamAwareEvaluationContext evaluationContext = new LazyParamAwareEvaluationContext(rootObject,
this.paramNameDiscoverer, method, args, targetClass, this.targetMethodCache);
if(result != NO_RESULT) {
evaluationContext.setVariable("result", result);
}
return condExp.getValue(evalContext, boolean.class);
return evaluationContext;
}
public Object key(String keyExpression, Method method, EvaluationContext evalContext) {
String key = toString(method, keyExpression);
Expression keyExp = this.keyCache.get(key);
if (keyExp == null) {
keyExp = this.parser.parseExpression(keyExpression);
this.keyCache.put(key, keyExp);
return getExpression(this.keyCache, keyExpression, method).getValue(evalContext);
}
public boolean condition(String conditionExpression, Method method, EvaluationContext evalContext) {
return getExpression(this.conditionCache, conditionExpression, method).getValue(
evalContext, boolean.class);
}
public boolean unless(String unlessExpression, Method method, EvaluationContext evalContext) {
return getExpression(this.unlessCache, unlessExpression, method).getValue(
evalContext, boolean.class);
}
private Expression getExpression(Map<String, Expression> cache, String expression, Method method) {
String key = toString(method, expression);
Expression rtn = cache.get(key);
if (rtn == null) {
rtn = this.parser.parseExpression(expression);
cache.put(key, rtn);
}
return keyExp.getValue(evalContext);
return rtn;
}
private String toString(Method method, String expression) {
@@ -89,4 +124,4 @@ class ExpressionEvaluator {
sb.append(expression);
return sb.toString();
}
}
}

View File

@@ -117,4 +117,4 @@ class LazyParamAwareEvaluationContext extends StandardEvaluationContext {
sb.append(m.toString());
return sb.toString();
}
}
}

View File

@@ -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.
@@ -26,7 +26,6 @@ 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.
@@ -45,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(), decorateCache(cache));
this.cacheNames.add(cache.getName());

View File

@@ -29,4 +29,4 @@ package org.springframework.context.annotation;
public enum AdviceMode {
PROXY,
ASPECTJ
}
}

View File

@@ -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,9 +39,10 @@ import static org.springframework.context.annotation.MetadataUtils.*;
*/
public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
private final ScopedProxyMode defaultProxyMode;
protected Class<? extends Annotation> scopeAnnotationType = Scope.class;
private final ScopedProxyMode defaultProxyMode;
/**
* Create a new instance of the {@code AnnotationScopeMetadataResolver} class.
@@ -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");

View File

@@ -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.
@@ -177,7 +177,7 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
* @see org.springframework.beans.factory.annotation.Autowired
* @see org.springframework.beans.factory.annotation.Value
*/
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
@@ -190,7 +190,7 @@ public @interface Bean {
String[] name() default {};
/**
* Are dependencies to be injected via autowiring?
* Are dependencies to be injected via convention-based autowiring by name or type?
*/
Autowire autowire() default Autowire.NO;

View File

@@ -725,4 +725,4 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
}
}
}
}

View File

@@ -68,8 +68,7 @@ import org.springframework.stereotype.Component;
* <beans>
* <context:annotation-config/>
* <bean class="com.acme.AppConfig"/>
* </beans>
* }</pre>
* </beans>}</pre>
*
* In the example above, {@code <context:annotation-config/>} is required in order to
* enable {@link ConfigurationClassPostProcessor} and other annotation-related

View File

@@ -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.
@@ -256,7 +256,8 @@ class ConfigurationClassBeanDefinitionReader {
if (proxyMode != ScopedProxyMode.NO) {
BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
new BeanDefinitionHolder(beanDef, beanName), this.registry, proxyMode == ScopedProxyMode.TARGET_CLASS);
beanDefToRegister = proxyDef.getBeanDefinition();
beanDefToRegister =
new ConfigurationClassBeanDefinition((RootBeanDefinition) proxyDef.getBeanDefinition(), configClass);
}
if (logger.isDebugEnabled()) {
@@ -307,12 +308,17 @@ class ConfigurationClassBeanDefinitionReader {
@SuppressWarnings("serial")
private static class ConfigurationClassBeanDefinition extends RootBeanDefinition implements AnnotatedBeanDefinition {
private AnnotationMetadata annotationMetadata;
private final AnnotationMetadata annotationMetadata;
public ConfigurationClassBeanDefinition(ConfigurationClass configClass) {
this.annotationMetadata = configClass.getMetadata();
}
public ConfigurationClassBeanDefinition(RootBeanDefinition original, ConfigurationClass configClass) {
super(original);
this.annotationMetadata = configClass.getMetadata();
}
private ConfigurationClassBeanDefinition(ConfigurationClassBeanDefinition original) {
super(original);
this.annotationMetadata = original.annotationMetadata;

View File

@@ -17,17 +17,11 @@
package org.springframework.context.annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.CallbackFilter;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.aop.scope.ScopedProxyFactoryBean;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.DisposableBean;
@@ -35,6 +29,12 @@ import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.SimpleInstantiationStrategy;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.CallbackFilter;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
@@ -52,8 +52,8 @@ class ConfigurationClassEnhancer {
private static final Log logger = LogFactory.getLog(ConfigurationClassEnhancer.class);
private static final Class<?>[] CALLBACK_TYPES = { BeanMethodInterceptor.class,
DisposableBeanMethodInterceptor.class, NoOp.class };
private static final Class<?>[] CALLBACK_TYPES = {BeanMethodInterceptor.class,
DisposableBeanMethodInterceptor.class, NoOp.class};
private static final CallbackFilter CALLBACK_FILTER = new CallbackFilter() {
public int accept(Method candidateMethod) {
@@ -80,10 +80,8 @@ class ConfigurationClassEnhancer {
public ConfigurationClassEnhancer(ConfigurableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
// Callback instances must be ordered in the same way as CALLBACK_TYPES and CALLBACK_FILTER
this.callbackInstances = new Callback[] {
new BeanMethodInterceptor(beanFactory),
DISPOSABLE_BEAN_METHOD_INTERCEPTOR,
NoOp.INSTANCE };
this.callbackInstances = new Callback[]
{new BeanMethodInterceptor(beanFactory), DISPOSABLE_BEAN_METHOD_INTERCEPTOR, NoOp.INSTANCE};
}
/**
@@ -169,9 +167,8 @@ class ConfigurationClassEnhancer {
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return beanFactory.getBean(beanName);
return this.beanFactory.getBean(this.beanName);
}
}
@@ -209,19 +206,16 @@ class ConfigurationClassEnhancer {
*/
private static class BeanMethodInterceptor implements MethodInterceptor {
private static final Class<?>[] CALLBACK_TYPES = {
GetObjectMethodInterceptor.class, NoOp.class };
private static final Class<?>[] CALLBACK_TYPES = {GetObjectMethodInterceptor.class, NoOp.class};
private static final CallbackFilter CALLBACK_FITLER = new CallbackFilter() {
private static final CallbackFilter CALLBACK_FILTER = new CallbackFilter() {
public int accept(Method method) {
return method.getName().equals("getObject") ? 0 : 1;
return (method.getName().equals("getObject") ? 0 : 1);
}
};
private final ConfigurableBeanFactory beanFactory;
public BeanMethodInterceptor(ConfigurableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@@ -229,7 +223,6 @@ class ConfigurationClassEnhancer {
/**
* Enhance a {@link Bean @Bean} method to check the supplied BeanFactory for the
* existence of this bean object.
*
* @throws Throwable as a catch-all for any exception that may be thrown when
* invoking the super implementation of the proxied method i.e., the actual
* {@code @Bean} method.
@@ -255,8 +248,8 @@ class ConfigurationClassEnhancer {
// proxy that intercepts calls to getObject() and returns any cached bean instance.
// this ensures that the semantics of calling a FactoryBean from within @Bean methods
// is the same as that of referring to a FactoryBean within XML. See SPR-6602.
if (factoryContainsBean('&'+beanName) && factoryContainsBean(beanName)) {
Object factoryBean = this.beanFactory.getBean('&'+beanName);
if (factoryContainsBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName) && factoryContainsBean(beanName)) {
Object factoryBean = this.beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
if (factoryBean instanceof ScopedProxyFactoryBean) {
// pass through - scoped proxy factory beans are a special case and should not
// be further proxied
@@ -267,9 +260,7 @@ class ConfigurationClassEnhancer {
}
}
boolean factoryIsCaller = beanMethod.equals(SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod());
boolean factoryAlreadyContainsSingleton = this.beanFactory.containsSingleton(beanName);
if (factoryIsCaller && !factoryAlreadyContainsSingleton) {
if (isCurrentlyInvokedFactoryMethod(beanMethod) && !this.beanFactory.containsSingleton(beanName)) {
// the factory is calling the bean method in order to instantiate and register the bean
// (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
// create the bean instance.
@@ -306,7 +297,7 @@ class ConfigurationClassEnhancer {
}
/**
* Check the beanFactory to see whether the bean named <var>beanName</var> already
* Check the BeanFactory to see whether the bean named <var>beanName</var> already
* exists. Accounts for the fact that the requested bean may be "in creation", i.e.:
* we're in the middle of servicing the initial request for this bean. From an enhanced
* factory method's perspective, this means that the bean does not actually yet exist,
@@ -319,9 +310,19 @@ class ConfigurationClassEnhancer {
* @return whether <var>beanName</var> already exists in the factory
*/
private boolean factoryContainsBean(String beanName) {
boolean containsBean = this.beanFactory.containsBean(beanName);
boolean currentlyInCreation = this.beanFactory.isCurrentlyInCreation(beanName);
return (containsBean && !currentlyInCreation);
return (this.beanFactory.containsBean(beanName) && !this.beanFactory.isCurrentlyInCreation(beanName));
}
/**
* Check whether the given method corresponds to the container's currently invoked
* factory method. Compares method name and parameter types only in order to work
* around a potential problem with covariant return types (currently only known
* to happen on Groovy classes).
*/
private boolean isCurrentlyInvokedFactoryMethod(Method method) {
Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
return (currentlyInvoked != null && method.getName().equals(currentlyInvoked.getName()) &&
Arrays.equals(method.getParameterTypes(), currentlyInvoked.getParameterTypes()));
}
/**
@@ -335,13 +336,12 @@ class ConfigurationClassEnhancer {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(fbClass);
enhancer.setUseFactory(false);
enhancer.setCallbackFilter(CALLBACK_FITLER);
enhancer.setCallbackFilter(CALLBACK_FILTER);
// Callback instances must be ordered in the same way as CALLBACK_TYPES and CALLBACK_FILTER
Callback[] callbackInstances = new Callback[] {
new GetObjectMethodInterceptor(this.beanFactory, beanName),
NoOp.INSTANCE
};
enhancer.setCallbackTypes(CALLBACK_TYPES);
Class<?> fbSubclass = enhancer.createClass();
Enhancer.registerCallbacks(fbSubclass, callbackInstances);

View File

@@ -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.
@@ -54,6 +54,7 @@ import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import static org.springframework.context.annotation.MetadataUtils.*;
@@ -228,7 +229,7 @@ class ConfigurationClassParser {
// process any @Import annotations
Set<String> imports = getImports(metadata.getClassName(), null, new HashSet<String>());
if (imports != null && !imports.isEmpty()) {
if (!CollectionUtils.isEmpty(imports)) {
processImport(configClass, imports.toArray(new String[imports.size()]), true);
}
@@ -292,9 +293,8 @@ class ConfigurationClassParser {
* @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 Set<String> getImports(String className, Set<String> imports,
Set<String> visited) throws IOException {
if (visited.add(className)) {
private Set<String> getImports(String className, Set<String> imports, Set<String> visited) throws IOException {
if (visited.add(className) && !className.startsWith("java")) {
AnnotationMetadata metadata = metadataReaderFactory.getMetadataReader(className).getAnnotationMetadata();
for (String annotationType : metadata.getAnnotationTypes()) {
imports = getImports(annotationType, imports, visited);
@@ -331,7 +331,7 @@ class ConfigurationClassParser {
throw new IllegalStateException(ex);
}
}
else if (new AssignableTypeFilter(ImportBeanDefinitionRegistrar.class).match(reader, metadataReaderFactory)) {
else if (new AssignableTypeFilter(ImportBeanDefinitionRegistrar.class).match(reader, this.metadataReaderFactory)) {
// the candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions
try {
ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(
@@ -360,17 +360,16 @@ class ConfigurationClassParser {
private void invokeAwareMethods(ImportBeanDefinitionRegistrar registrar) {
if (registrar instanceof Aware) {
if (registrar instanceof ResourceLoaderAware) {
((ResourceLoaderAware) registrar).setResourceLoader(resourceLoader);
((ResourceLoaderAware) registrar).setResourceLoader(this.resourceLoader);
}
if (registrar instanceof BeanClassLoaderAware) {
ClassLoader classLoader =
registry instanceof ConfigurableBeanFactory ?
((ConfigurableBeanFactory) registry).getBeanClassLoader() :
resourceLoader.getClassLoader();
ClassLoader classLoader = (this.registry instanceof ConfigurableBeanFactory ?
((ConfigurableBeanFactory) this.registry).getBeanClassLoader() :
this.resourceLoader.getClassLoader());
((BeanClassLoaderAware) registrar).setBeanClassLoader(classLoader);
}
if (registrar instanceof BeanFactoryAware && registry instanceof BeanFactory) {
((BeanFactoryAware) registrar).setBeanFactory((BeanFactory) registry);
if (registrar instanceof BeanFactoryAware && this.registry instanceof BeanFactory) {
((BeanFactoryAware) registrar).setBeanFactory((BeanFactory) this.registry);
}
}
}
@@ -398,6 +397,7 @@ class ConfigurationClassParser {
return this.importStack;
}
interface ImportRegistry {
String getImportingClassFor(String importedClass);
@@ -470,4 +470,5 @@ class ConfigurationClassParser {
new Location(importStack.peek().getResource(), metadata));
}
}
}

View File

@@ -110,4 +110,4 @@ public @interface EnableAspectJAutoProxy {
*/
boolean proxyTargetClass() default false;
}
}

View File

@@ -60,4 +60,4 @@ public interface ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
}

View File

@@ -44,4 +44,4 @@ class PropertyOverrideBeanDefinitionParser extends AbstractPropertyLoadingBeanDe
}
}
}

View File

@@ -87,7 +87,6 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
@SuppressWarnings("unchecked")
public void run() {
listener.onApplicationEvent(event);
}

View File

@@ -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.
@@ -17,7 +17,6 @@
package org.springframework.context.support;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
@@ -114,12 +113,13 @@ public abstract class AbstractRefreshableConfigApplicationContext extends Abstra
/**
* Resolve the given path, replacing placeholders with corresponding
* system property values if necessary. Applied to config locations.
* environment property values if necessary. Applied to config locations.
* @param path the original file path
* @return the resolved file path
* @see org.springframework.core.env.Environment#resolveRequiredPlaceholders(String)
*/
protected String resolvePath(String path) {
return this.getEnvironment().resolveRequiredPlaceholders(path);
return getEnvironment().resolveRequiredPlaceholders(path);
}

View File

@@ -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.
@@ -70,9 +70,9 @@ public class MessageSourceResourceBundle extends ResourceBundle {
* 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,12 +80,29 @@ public class MessageSourceResourceBundle extends ResourceBundle {
}
/**
* This implementation returns {@code null}, 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");
}
/**

View File

@@ -135,7 +135,7 @@ public class ResourceBundleMessageSource extends AbstractMessageSource implement
* @see #setBasename
* @see java.util.ResourceBundle#getBundle(String)
*/
public void setBasenames(String... basenames) {
public void setBasenames(String... basenames) {
if (basenames != null) {
this.basenames = new String[basenames.length];
for (int i = 0; i < basenames.length; i++) {

View File

@@ -110,7 +110,7 @@ public class SimpleRemoteSlsbInvokerInterceptor extends AbstractRemoteSlsbInvoke
}
else if (targetEx instanceof CreateException) {
throw RmiClientInterceptorUtils.convertRmiAccessException(
invocation.getMethod(), targetEx, "Could not create remote EJB [" + getJndiName() + "]");
invocation.getMethod(), targetEx, "Could not create remote EJB [" + getJndiName() + "]");
}
throw targetEx;
}

View File

@@ -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.
@@ -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);
}

View File

@@ -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.
@@ -40,24 +40,28 @@ import org.springframework.util.Assert;
public class DateFormatterRegistrar implements FormatterRegistrar {
private DateFormatter dateFormatter = new DateFormatter();
private DateFormatter dateFormatter;
public void registerFormatters(FormatterRegistry registry) {
addDateConverters(registry);
registry.addFormatter(dateFormatter);
registry.addFormatterForFieldType(Calendar.class, dateFormatter);
registry.addFormatterForFieldAnnotation(new DateTimeFormatAnnotationFormatterFactory());
// In order to retain back compatibility we only register Date/Calendar
// types when a user defined formatter is specified (see SPR-10105)
if(this.dateFormatter != null) {
registry.addFormatter(this.dateFormatter);
registry.addFormatterForFieldType(Calendar.class, this.dateFormatter);
}
}
/**
* Set the date formatter to register. If not specified the default {@link DateFormatter}
* will be used. This method can be used if additional formatter configuration is
* required.
* Set the date formatter to register. If not specified no formatter is registered.
* This method can be used if global formatter configuration is required.
* @param dateFormatter the date formatter
*/
public void setFormatter(DateFormatter dateFormatter) {
Assert.notNull(dateFormatter,"DateFormatter must not be null");
Assert.notNull(dateFormatter, "DateFormatter must not be null");
this.dateFormatter = dateFormatter;
}
@@ -117,7 +121,7 @@ public class DateFormatterRegistrar implements FormatterRegistrar {
private DateToCalendarConverter dateToCalendarConverter = new DateToCalendarConverter();
public Calendar convert(Long source) {
return dateToCalendarConverter.convert(new Date(source));
return this.dateToCalendarConverter.convert(new Date(source));
}
}
}

View File

@@ -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.
@@ -35,6 +35,7 @@ import org.springframework.format.datetime.DateFormatterRegistrar;
* Installs lower-level type converters required to integrate Joda Time support into Spring's field formatting system.
*
* @author Keith Donald
* @author Phillip Webb
* @since 3.0
*/
final class JodaTimeConverters {
@@ -55,6 +56,7 @@ final class JodaTimeConverters {
registry.addConverter(new DateTimeToCalendarConverter());
registry.addConverter(new DateTimeToLongConverter());
registry.addConverter(new CalendarToReadableInstantConverter());
registry.addConverter(new DateToReadableInstantConverter());
}
@@ -159,4 +161,14 @@ final class JodaTimeConverters {
}
}
/**
* Used when printing a java.util.Date field with a ReadableInstantPrinter.
* @see MillisecondInstantPrinter
* @see JodaDateTimeFormatAnnotationFormatterFactory
*/
private static class DateToReadableInstantConverter implements Converter<Date, ReadableInstant> {
public ReadableInstant convert(Date source) {
return new DateTime(source);
}
}
}

View File

@@ -75,7 +75,7 @@ public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
* Default is {@link DateTimeFormat#shortDate()}.
*/
public void setDateStyle(String dateStyle) {
factories.get(Type.DATE).setStyle(dateStyle+"-");
this.factories.get(Type.DATE).setStyle(dateStyle+"-");
}
/**
@@ -83,7 +83,7 @@ public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
* Default is {@link DateTimeFormat#shortTime()}.
*/
public void setTimeStyle(String timeStyle) {
factories.get(Type.TIME).setStyle("-"+timeStyle);
this.factories.get(Type.TIME).setStyle("-"+timeStyle);
}
/**
@@ -92,7 +92,7 @@ public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
* Default is {@link DateTimeFormat#shortDateTime()}.
*/
public void setDateTimeStyle(String dateTimeStyle) {
factories.get(Type.DATE_TIME).setStyle(dateTimeStyle);
this.factories.get(Type.DATE_TIME).setStyle(dateTimeStyle);
}
/**
@@ -101,9 +101,9 @@ public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
* If set to true, the dateStyle, timeStyle, and dateTimeStyle properties are ignored.
*/
public void setUseIsoFormat(boolean useIsoFormat) {
factories.get(Type.DATE).setIso(useIsoFormat ? ISO.DATE : null);
factories.get(Type.TIME).setIso(useIsoFormat ? ISO.TIME : null);
factories.get(Type.DATE_TIME).setIso(useIsoFormat ? ISO.DATE_TIME : null);
this.factories.get(Type.DATE).setIso(useIsoFormat ? ISO.DATE : null);
this.factories.get(Type.TIME).setIso(useIsoFormat ? ISO.TIME : null);
this.factories.get(Type.DATE_TIME).setIso(useIsoFormat ? ISO.DATE_TIME : null);
}
/**
@@ -174,19 +174,28 @@ public class JodaTimeFormatterRegistrar implements FormatterRegistrar {
addFormatterForFields(registry,
new ReadableInstantPrinter(dateTimeFormatter),
new DateTimeParser(dateTimeFormatter),
ReadableInstant.class, Date.class, Calendar.class);
ReadableInstant.class);
// In order to retain back compatibility we only register Date/Calendar
// types when a user defined formatter is specified (see SPR-10105)
if(this.formatters.containsKey(Type.DATE_TIME)) {
addFormatterForFields(registry,
new ReadableInstantPrinter(dateTimeFormatter),
new DateTimeParser(dateTimeFormatter),
Date.class, Calendar.class);
}
registry.addFormatterForFieldAnnotation(
new JodaDateTimeFormatAnnotationFormatterFactory());
}
private DateTimeFormatter getFormatter(Type type) {
DateTimeFormatter formatter = formatters.get(type);
DateTimeFormatter formatter = this.formatters.get(type);
if(formatter != null) {
return formatter;
}
DateTimeFormatter fallbackFormatter = getFallbackFormatter(type);
return factories.get(type).createDateTimeFormatter(fallbackFormatter );
return this.factories.get(type).createDateTimeFormatter(fallbackFormatter );
}
private DateTimeFormatter getFallbackFormatter(Type type) {

View File

@@ -195,7 +195,7 @@ public class FormattingConversionService extends GenericConversionService
private Class<? extends Annotation> annotationType;
private AnnotationFormatterFactory annotationFormatterFactory;
private AnnotationFormatterFactory annotationFormatterFactory;
private Class<?> fieldType;

View File

@@ -116,7 +116,7 @@ public class FormattingConversionServiceFactoryBean
/**
* Indicate whether default formatters should be registered or not.
* <p>By default, built-in formatters are registered. This flag can be used
* to turn that off and rely on explicitly registered formatters only.
* to turn that off and rely on explicitly registered formatters only.
* @see #setFormatters(Set)
* @see #setFormatterRegistrars(Set)
*/

View File

@@ -135,4 +135,4 @@ public class ReflectiveLoadTimeWeaver implements LoadTimeWeaver {
return new SimpleThrowawayClassLoader(this.classLoader);
}
}
}
}

View File

@@ -29,7 +29,7 @@ import org.springframework.core.OverridingClassLoader;
* @author Costin Leau
* @since 2.0
*/
public class SimpleInstrumentableClassLoader extends OverridingClassLoader {
public class SimpleInstrumentableClassLoader extends OverridingClassLoader {
private final WeavingTransformer weavingTransformer;

View File

@@ -142,4 +142,4 @@ class JBossMCAdapter implements JBossClassLoaderAdapter {
public ClassLoader getInstrumentableClassLoader() {
return classLoader;
}
}
}

View File

@@ -79,4 +79,4 @@ class JBossMCTranslatorAdapter implements InvocationHandler {
builder.append(this.transformer);
return builder.toString();
}
}
}

View File

@@ -68,4 +68,4 @@ class JBossModulesAdapter implements JBossClassLoaderAdapter {
public ClassLoader getInstrumentableClassLoader() {
return classLoader;
}
}
}

View File

@@ -85,4 +85,4 @@ class OC4JClassLoaderAdapter {
throw new IllegalStateException("Could not copy OC4J classloader", ex);
}
}
}
}

View File

@@ -92,4 +92,4 @@ class OC4JClassPreprocessorAdapter implements InvocationHandler {
builder.append(this.transformer);
return builder.toString();
}
}
}

View File

@@ -76,5 +76,4 @@ public class OC4JLoadTimeWeaver implements LoadTimeWeaver {
public ClassLoader getThrowawayClassLoader() {
return this.classLoader.getThrowawayClassLoader();
}
}

View File

@@ -110,4 +110,4 @@ class WebLogicClassLoaderAdapter {
throw new IllegalStateException("Could not construct WebLogic GenericClassLoader", ex);
}
}
}
}

View File

@@ -87,4 +87,4 @@ class WebLogicClassPreProcessorAdapter implements InvocationHandler {
builder.append(this.transformer);
return builder.toString();
}
}
}

View File

@@ -69,4 +69,4 @@ public class WebLogicLoadTimeWeaver implements LoadTimeWeaver {
public ClassLoader getThrowawayClassLoader() {
return this.classLoader.getThrowawayClassLoader();
}
}
}

View File

@@ -58,7 +58,7 @@ public class SpringModelMBean extends RequiredModelMBean {
* Construct a new SpringModelMBean instance with the given {@link ModelMBeanInfo}.
* @see javax.management.modelmbean.RequiredModelMBean#RequiredModelMBean(ModelMBeanInfo)
*/
public SpringModelMBean(ModelMBeanInfo mbi) throws MBeanException, RuntimeOperationsException {
public SpringModelMBean(ModelMBeanInfo mbi) throws MBeanException, RuntimeOperationsException {
super(mbi);
}

View File

@@ -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.
@@ -30,6 +30,8 @@ import javax.management.modelmbean.ModelMBeanOperationInfo;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.jmx.support.JmxUtils;
/**
@@ -49,6 +51,7 @@ import org.springframework.jmx.support.JmxUtils;
*
* @author Rob Harrop
* @author Juergen Hoeller
* @author David Boden
* @since 1.2
* @see #includeOperation
* @see #includeReadAttribute
@@ -177,6 +180,8 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean
private boolean exposeClassDescriptor = false;
private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
/**
* Set the default for the JMX field "currencyTimeLimit".
@@ -254,6 +259,23 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean
return this.exposeClassDescriptor;
}
/**
* Set the ParameterNameDiscoverer to use for resolving method parameter
* names if needed (e.g. for parameter names of MBean operation methods).
* <p>The default is {@link LocalVariableTableParameterNameDiscoverer}.
*/
public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
this.parameterNameDiscoverer = parameterNameDiscoverer;
}
/**
* Return the ParameterNameDiscoverer to use for resolving method parameter
* names if needed (may be {@code null} in order to skip parameter detection).
*/
protected ParameterNameDiscoverer getParameterNameDiscoverer() {
return this.parameterNameDiscoverer;
}
/**
* Iterate through all properties on the MBean class and gives subclasses
@@ -381,7 +403,8 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean
* Creates an instance of {@code ModelMBeanOperationInfo} for the
* given method. Populates the parameter info for the operation.
* @param method the {@code Method} to create a {@code ModelMBeanOperationInfo} for
* @param name the name for the operation info
* @param name the logical name for the operation (method name or property name);
* not used by the default implementation but possibly by subclasses
* @param beanKey the key associated with the MBean in the beans map
* of the {@code MBeanExporter}
* @return the {@code ModelMBeanOperationInfo}
@@ -392,7 +415,7 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean
return new ModelMBeanOperationInfo(getOperationDescription(method, beanKey), method);
}
else {
return new ModelMBeanOperationInfo(name,
return new ModelMBeanOperationInfo(method.getName(),
getOperationDescription(method, beanKey),
getOperationParameters(method, beanKey),
method.getReturnType().getName(),
@@ -476,16 +499,27 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean
/**
* Create parameter info for the given method.
* <p>The default implementation returns an empty arry of {@code MBeanParameterInfo}.
* <p>The default implementation returns an empty array of {@code MBeanParameterInfo}.
* @param method the {@code Method} to get the parameter information for
* @param beanKey the key associated with the MBean in the beans map
* of the {@code MBeanExporter}
* @return the {@code MBeanParameterInfo} array
*/
protected MBeanParameterInfo[] getOperationParameters(Method method, String beanKey) {
return new MBeanParameterInfo[0];
}
ParameterNameDiscoverer paramNameDiscoverer = getParameterNameDiscoverer();
String[] paramNames = (paramNameDiscoverer != null ? paramNameDiscoverer.getParameterNames(method) : null);
if (paramNames == null) {
return new MBeanParameterInfo[0];
}
MBeanParameterInfo[] info = new MBeanParameterInfo[paramNames.length];
Class<?>[] typeParameters = method.getParameterTypes();
for(int i = 0; i < info.length; i++) {
info[i] = new MBeanParameterInfo(paramNames[i], typeParameters[i].getName(), paramNames[i]);
}
return info;
}
/**
* Allows subclasses to add extra fields to the {@code Descriptor} for an MBean.

View File

@@ -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.
@@ -36,6 +36,7 @@ import org.springframework.jmx.export.metadata.ManagedOperationParameter;
import org.springframework.jmx.export.metadata.ManagedResource;
import org.springframework.jmx.support.MetricType;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
@@ -255,19 +256,17 @@ public class MetadataMBeanInfoAssembler extends AbstractReflectiveMBeanInfoAssem
@Override
protected MBeanParameterInfo[] getOperationParameters(Method method, String beanKey) {
ManagedOperationParameter[] params = this.attributeSource.getManagedOperationParameters(method);
if (params == null || params.length == 0) {
return new MBeanParameterInfo[0];
if (ObjectUtils.isEmpty(params)) {
return super.getOperationParameters(method, beanKey);
}
MBeanParameterInfo[] parameterInfo = new MBeanParameterInfo[params.length];
Class[] methodParameters = method.getParameterTypes();
for (int i = 0; i < params.length; i++) {
ManagedOperationParameter param = params[i];
parameterInfo[i] =
new MBeanParameterInfo(param.getName(), methodParameters[i].getName(), param.getDescription());
}
return parameterInfo;
}

View File

@@ -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,6 +16,7 @@
package org.springframework.jmx.support;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.management.InstanceAlreadyExistsException;
@@ -112,7 +113,7 @@ public class MBeanRegistrationSupport {
/**
* The beans that have been registered by this exporter.
*/
protected final Set<ObjectName> registeredBeans = new LinkedHashSet<ObjectName>();
private final Set<ObjectName> registeredBeans = Collections.synchronizedSet(new LinkedHashSet<ObjectName>());
/**
* The policy used when registering an MBean and finding that it already exists.
@@ -228,10 +229,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();
}
/**
@@ -257,6 +257,7 @@ public class MBeanRegistrationSupport {
logger.error("Could not unregister MBean [" + objectName + "]", ex);
}
}
this.registeredBeans.remove(objectName);
}
/**

View File

@@ -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.
@@ -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;
@@ -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() + "]";

View File

@@ -345,7 +345,7 @@ public class RmiClientInterceptor extends RemoteInvocationBasedAccessor
}
catch (RemoteException ex) {
throw RmiClientInterceptorUtils.convertRmiAccessException(
invocation.getMethod(), ex, isConnectFailure(ex), getServiceUrl());
invocation.getMethod(), ex, isConnectFailure(ex), getServiceUrl());
}
catch (InvocationTargetException ex) {
Throwable exToThrow = ex.getTargetException();
@@ -389,7 +389,7 @@ public class RmiClientInterceptor extends RemoteInvocationBasedAccessor
* @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 [" + getServiceUrl() + "]";

View File

@@ -456,4 +456,4 @@ public class RmiServiceExporter extends RmiBasedExporter implements Initializing
}
}
}
}
}

View File

@@ -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,7 +39,7 @@ public class AnnotationAsyncExecutionInterceptor extends AsyncExecutionIntercept
/**
* Create a new {@code AnnotationAsyncExecutionInterceptor} with the given executor.
* @param defaultExecutor the executor to be used by default if no more specific
* executor has been qualified at the method level using {@link Async#value()}.
* executor has been qualified at the method level using {@link Async#value()}
*/
public AnnotationAsyncExecutionInterceptor(Executor defaultExecutor) {
super(defaultExecutor);
@@ -64,7 +64,7 @@ public class AnnotationAsyncExecutionInterceptor extends AsyncExecutionIntercept
if (async == null) {
async = AnnotationUtils.findAnnotation(method.getDeclaringClass(), Async.class);
}
return async == null ? null : async.value();
return (async != null ? async.value() : null);
}
}

View File

@@ -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.
@@ -28,7 +28,6 @@ import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
@@ -58,8 +57,6 @@ public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements B
private Pointcut pointcut;
private BeanFactory beanFactory;
/**
* Create a new {@code AsyncAnnotationAdvisor} for bean-style configuration.
@@ -84,30 +81,15 @@ public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements B
// If EJB 3.1 API not present, simply ignore.
}
this.advice = buildAdvice(executor);
this.setTaskExecutor(executor);
this.pointcut = buildPointcut(asyncAnnotationTypes);
}
/**
* Set the {@code BeanFactory} to be used when looking up executors by qualifier.
*/
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
delegateBeanFactory(beanFactory);
}
public void delegateBeanFactory(BeanFactory beanFactory) {
if (this.advice instanceof AnnotationAsyncExecutionInterceptor) {
((AnnotationAsyncExecutionInterceptor)this.advice).setBeanFactory(beanFactory);
}
}
/**
* Specify the task executor to use for asynchronous methods.
* Specify the default task executor to use for asynchronous methods.
*/
public void setTaskExecutor(Executor executor) {
this.advice = buildAdvice(executor);
delegateBeanFactory(this.beanFactory);
}
/**
@@ -126,6 +108,15 @@ public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements B
this.pointcut = buildPointcut(asyncAnnotationTypes);
}
/**
* Set the {@code BeanFactory} to be used when looking up executors by qualifier.
*/
public void setBeanFactory(BeanFactory beanFactory) {
if (this.advice instanceof BeanFactoryAware) {
((BeanFactoryAware) this.advice).setBeanFactory(beanFactory);
}
}
public Advice getAdvice() {
return this.advice;

View File

@@ -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.
@@ -64,18 +64,41 @@ public @interface Scheduled {
*/
long fixedDelay() default -1;
/**
* Execute the annotated method with a fixed period between the end
* of the last invocation and the start of the next.
* @return the delay in milliseconds as a String value, e.g. a placeholder
* @since 3.2.2
*/
String fixedDelayString() default "";
/**
* Execute the annotated method with a fixed period between invocations.
* @return the period in milliseconds
*/
long fixedRate() default -1;
/**
* Execute the annotated method with a fixed period between invocations.
* @return the period in milliseconds as a String value, e.g. a placeholder
* @since 3.2.2
*/
String fixedRateString() default "";
/**
* Number of milliseconds to delay before the first execution of a
* {@link #fixedRate()} or {@link #fixedDelay()} task.
* @return the initial delay in milliseconds
* @since 3.2
*/
long initialDelay() default 0;
long initialDelay() default -1;
/**
* Number of milliseconds to delay before the first execution of a
* {@link #fixedRate()} or {@link #fixedDelay()} task.
* @return the initial delay in milliseconds as a String value, e.g. a placeholder
* @since 3.2.2
*/
String initialDelayString() default "";
}

View File

@@ -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.
@@ -17,7 +17,6 @@
package org.springframework.scheduling.annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
@@ -111,53 +110,115 @@ public class ScheduledAnnotationBeanPostProcessor
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Scheduled annotation = AnnotationUtils.getAnnotation(method, Scheduled.class);
if (annotation != null) {
Assert.isTrue(void.class.equals(method.getReturnType()),
"Only void-returning methods may be annotated with @Scheduled.");
Assert.isTrue(method.getParameterTypes().length == 0,
"Only no-arg methods may be annotated with @Scheduled.");
if (AopUtils.isJdkDynamicProxy(bean)) {
try {
// found a @Scheduled method on the target class for this JDK proxy -> is it
// also present on the proxy itself?
method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
try {
Assert.isTrue(void.class.equals(method.getReturnType()),
"Only void-returning methods may be annotated with @Scheduled");
Assert.isTrue(method.getParameterTypes().length == 0,
"Only no-arg methods may be annotated with @Scheduled");
if (AopUtils.isJdkDynamicProxy(bean)) {
try {
// found a @Scheduled method on the target class for this JDK proxy -> is it
// also present on the proxy itself?
method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
}
catch (SecurityException ex) {
ReflectionUtils.handleReflectionException(ex);
}
catch (NoSuchMethodException ex) {
throw new IllegalStateException(String.format(
"@Scheduled method '%s' found on bean target class '%s', " +
"but not found in any interface(s) for bean JDK proxy. Either " +
"pull the method up to an interface or switch to subclass (CGLIB) " +
"proxies by setting proxy-target-class/proxyTargetClass " +
"attribute to 'true'", method.getName(), targetClass.getSimpleName()));
}
}
catch (SecurityException ex) {
ReflectionUtils.handleReflectionException(ex);
Runnable runnable = new ScheduledMethodRunnable(bean, method);
boolean processedSchedule = false;
String errorMessage = "Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required";
// Determine initial delay
long initialDelay = annotation.initialDelay();
String initialDelayString = annotation.initialDelayString();
if (!"".equals(initialDelayString)) {
Assert.isTrue(initialDelay < 0, "Specify 'initialDelay' or 'initialDelayString', not both");
if (embeddedValueResolver != null) {
initialDelayString = embeddedValueResolver.resolveStringValue(initialDelayString);
}
try {
initialDelay = Integer.parseInt(initialDelayString);
}
catch (NumberFormatException ex) {
throw new IllegalArgumentException(
"Invalid initialDelayString value \"" + initialDelayString + "\" - cannot parse into integer");
}
}
catch (NoSuchMethodException ex) {
throw new IllegalStateException(String.format(
"@Scheduled method '%s' found on bean target class '%s', " +
"but not found in any interface(s) for bean JDK proxy. Either " +
"pull the method up to an interface or switch to subclass (CGLIB) " +
"proxies by setting proxy-target-class/proxyTargetClass " +
"attribute to 'true'", method.getName(), targetClass.getSimpleName()));
// Check cron expression
String cron = annotation.cron();
if (!"".equals(cron)) {
Assert.isTrue(initialDelay == -1, "'initialDelay' not supported for cron triggers");
processedSchedule = true;
if (embeddedValueResolver != null) {
cron = embeddedValueResolver.resolveStringValue(cron);
}
registrar.addCronTask(new CronTask(runnable, cron));
}
}
Runnable runnable = new ScheduledMethodRunnable(bean, method);
boolean processedSchedule = false;
String errorMessage = "Exactly one of the 'cron', 'fixedDelay', or 'fixedRate' attributes is required.";
String cron = annotation.cron();
if (!"".equals(cron)) {
processedSchedule = true;
if (embeddedValueResolver != null) {
cron = embeddedValueResolver.resolveStringValue(cron);
// At this point we don't need to differentiate between initial delay set or not anymore
if (initialDelay < 0) {
initialDelay = 0;
}
registrar.addCronTask(new CronTask(runnable, cron));
// Check fixed delay
long fixedDelay = annotation.fixedDelay();
if (fixedDelay >= 0) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
registrar.addFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay));
}
String fixedDelayString = annotation.fixedDelayString();
if (!"".equals(fixedDelayString)) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
if (embeddedValueResolver != null) {
fixedDelayString = embeddedValueResolver.resolveStringValue(fixedDelayString);
}
try {
fixedDelay = Integer.parseInt(fixedDelayString);
}
catch (NumberFormatException ex) {
throw new IllegalArgumentException(
"Invalid fixedDelayString value \"" + fixedDelayString + "\" - cannot parse into integer");
}
registrar.addFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay));
}
// Check fixed rate
long fixedRate = annotation.fixedRate();
if (fixedRate >= 0) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
registrar.addFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay));
}
String fixedRateString = annotation.fixedRateString();
if (!"".equals(fixedRateString)) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
if (embeddedValueResolver != null) {
fixedRateString = embeddedValueResolver.resolveStringValue(fixedRateString);
}
try {
fixedRate = Integer.parseInt(fixedRateString);
}
catch (NumberFormatException ex) {
throw new IllegalArgumentException(
"Invalid fixedRateString value \"" + fixedRateString + "\" - cannot parse into integer");
}
registrar.addFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay));
}
// Check whether we had any attribute set
Assert.isTrue(processedSchedule, errorMessage);
}
long initialDelay = annotation.initialDelay();
long fixedDelay = annotation.fixedDelay();
if (fixedDelay >= 0) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
registrar.addFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay));
catch (IllegalArgumentException ex) {
throw new IllegalStateException(
"Encountered invalid @Scheduled method '" + method.getName() + "': " + ex.getMessage());
}
long fixedRate = annotation.fixedRate();
if (fixedRate >= 0) {
Assert.isTrue(!processedSchedule, errorMessage);
processedSchedule = true;
registrar.addFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay));
}
Assert.isTrue(processedSchedule, errorMessage);
}
}
});
@@ -168,18 +229,14 @@ public class ScheduledAnnotationBeanPostProcessor
if (event.getApplicationContext() != this.applicationContext) {
return;
}
Map<String, SchedulingConfigurer> configurers =
this.applicationContext.getBeansOfType(SchedulingConfigurer.class);
if (this.scheduler != null) {
this.registrar.setScheduler(this.scheduler);
}
for (SchedulingConfigurer configurer : configurers.values()) {
configurer.configureTasks(this.registrar);
}
if (this.registrar.hasTasks() && this.registrar.getScheduler() == null) {
Map<String, ? super Object> schedulers = new HashMap<String, Object>();
schedulers.putAll(applicationContext.getBeansOfType(TaskScheduler.class));
@@ -199,7 +256,6 @@ public class ScheduledAnnotationBeanPostProcessor
"configureTasks() callback. Found the following beans: " + schedulers.keySet());
}
}
this.registrar.afterPropertiesSet();
}

View File

@@ -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.
@@ -20,6 +20,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -54,6 +55,8 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
private boolean waitForTasksToCompleteOnShutdown = false;
private int awaitTerminationSeconds = 0;
private String beanName;
private ExecutorService executor;
@@ -85,9 +88,17 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
}
/**
* Set whether to wait for scheduled tasks to complete on shutdown.
* <p>Default is "false". Switch this to "true" if you prefer
* fully completed tasks at the expense of a longer shutdown phase.
* Set whether to wait for scheduled tasks to complete on shutdown,
* not interrupting running tasks and executing all tasks in the queue.
* <p>Default is "false", shutting down immediately through interrupting
* ongoing tasks and clearing the queue. Switch this flag to "true" if you
* prefer fully completed tasks at the expense of a longer shutdown phase.
* <p>Note that Spring's container shutdown continues while ongoing tasks
* are being completed. If you want this executor to block and wait for the
* termination of tasks before the rest of the container continues to shut
* down - e.g. in order to keep up other resources that your tasks may need -,
* set the {@link #setAwaitTerminationSeconds "awaitTerminationSeconds"}
* property instead of or in addition to this property.
* @see java.util.concurrent.ExecutorService#shutdown()
* @see java.util.concurrent.ExecutorService#shutdownNow()
*/
@@ -95,6 +106,33 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
this.waitForTasksToCompleteOnShutdown = waitForJobsToCompleteOnShutdown;
}
/**
* Set the maximum number of seconds that this executor is supposed to block
* on shutdown in order to wait for remaining tasks to complete their execution
* before the rest of the container continues to shut down. This is particularly
* useful if your remaining tasks are likely to need access to other resources
* that are also managed by the container.
* <p>By default, this executor won't wait for the termination of tasks at all.
* It will either shut down immediately, interrupting ongoing tasks and clearing
* the remaining task queue - or, if the
* {@link #setWaitForTasksToCompleteOnShutdown "waitForTasksToCompleteOnShutdown"}
* flag has been set to {@code true}, it will continue to fully execute all
* ongoing tasks as well as all remaining tasks in the queue, in parallel to
* the rest of the container shutting down.
* <p>In either case, if you specify an await-termination period using this property,
* this executor will wait for the given time (max) for the termination of tasks.
* As a rule of thumb, specify a significantly higher timeout here if you set
* "waitForTasksToCompleteOnShutdown" to {@code true} at the same time,
* since all remaining tasks in the queue will still get executed - in contrast
* to the default shutdown behavior where it's just about waiting for currently
* executing tasks that aren't reacting to thread interruption.
* @see java.util.concurrent.ExecutorService#shutdown()
* @see java.util.concurrent.ExecutorService#awaitTermination
*/
public void setAwaitTerminationSeconds(int awaitTerminationSeconds) {
this.awaitTerminationSeconds = awaitTerminationSeconds;
}
public void setBeanName(String name) {
this.beanName = name;
}
@@ -145,6 +183,8 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
/**
* Perform a shutdown on the ThreadPoolExecutor.
* @see java.util.concurrent.ExecutorService#shutdown()
* @see java.util.concurrent.ExecutorService#shutdownNow()
* @see #awaitTerminationIfNecessary()
*/
public void shutdown() {
if (logger.isInfoEnabled()) {
@@ -156,6 +196,31 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
else {
this.executor.shutdownNow();
}
awaitTerminationIfNecessary();
}
/**
* Wait for the executor to terminate, according to the value of the
* {@link #setAwaitTerminationSeconds "awaitTerminationSeconds"} property.
*/
private void awaitTerminationIfNecessary() {
if (this.awaitTerminationSeconds > 0) {
try {
if (!this.executor.awaitTermination(this.awaitTerminationSeconds, TimeUnit.SECONDS)) {
if (logger.isWarnEnabled()) {
logger.warn("Timed out while waiting for executor" +
(this.beanName != null ? " '" + this.beanName + "'" : "") + " to terminate");
}
}
}
catch (InterruptedException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Interrupted while waiting for executor" +
(this.beanName != null ? " '" + this.beanName + "'" : "") + " to terminate");
}
Thread.currentThread().interrupt();
}
}
}
}

View File

@@ -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.
@@ -126,7 +126,7 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport
* <p>Default is "false", exposing the raw executor as bean reference.
* Switch this flag to "true" to strictly prevent clients from
* modifying the executor's configuration.
* @see java.util.concurrent.Executors#unconfigurableScheduledExecutorService
* @see java.util.concurrent.Executors#unconfigurableExecutorService
*/
public void setExposeUnconfigurableExecutor(boolean exposeUnconfigurableExecutor) {
this.exposeUnconfigurableExecutor = exposeUnconfigurableExecutor;
@@ -137,9 +137,8 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport
ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
BlockingQueue<Runnable> queue = createQueue(this.queueCapacity);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
queue, threadFactory, rejectedExecutionHandler);
ThreadPoolExecutor executor = createExecutor(this.corePoolSize, this.maxPoolSize,
this.keepAliveSeconds, queue, threadFactory, rejectedExecutionHandler);
if (this.allowCoreThreadTimeOut) {
executor.allowCoreThreadTimeOut(true);
}
@@ -151,6 +150,27 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport
return executor;
}
/**
* Create a new instance of {@link ThreadPoolExecutor} or a subclass thereof.
* <p>The default implementation creates a standard {@link ThreadPoolExecutor}.
* Can be overridden to provide custom {@link ThreadPoolExecutor} subclasses.
* @param corePoolSize the specified core pool size
* @param maxPoolSize the specified maximum pool size
* @param keepAliveSeconds the specified keep-alive time in seconds
* @param queue the BlockingQueue to use
* @param threadFactory the ThreadFactory to use
* @param rejectedExecutionHandler the RejectedExecutionHandler to use
* @return a new ThreadPoolExecutor instance
* @see #afterPropertiesSet()
*/
protected ThreadPoolExecutor createExecutor(
int corePoolSize, int maxPoolSize, int keepAliveSeconds, BlockingQueue<Runnable> queue,
ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
return new ThreadPoolExecutor(corePoolSize, maxPoolSize,
keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler);
}
/**
* Create the BlockingQueue to use for the ThreadPoolExecutor.
* <p>A LinkedBlockingQueue instance will be created for a positive

View File

@@ -0,0 +1,65 @@
/*
* 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.
* 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.scheduling.config;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
/**
* {@link ScheduledTaskRegistrar} subclass that redirects the actual scheduling
* of tasks to the {@link ContextRefreshedEvent} callback. Falls back to regular
* {@code ScheduledTaskRegistrar} behavior when not running within an ApplicationContext.
*
* @author Juergen Hoeller
* @since 3.2.1
*/
public class ContextLifecycleScheduledTaskRegistrar extends ScheduledTaskRegistrar
implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent> {
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* If we're running within an ApplicationContext, don't schedule the tasks
* right here; wait for this context's ContextRefreshedEvent instead.
*/
@Override
public void afterPropertiesSet() {
if (this.applicationContext == null) {
scheduleTasks();
}
}
/**
* Actually schedule the tasks at the right time of the context lifecycle,
* if we're running within an ApplicationContext.
*/
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getApplicationContext() != this.applicationContext) {
return;
}
scheduleTasks();
}
}

View File

@@ -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.
@@ -44,8 +44,8 @@ public class IntervalTask extends Task {
*/
public IntervalTask(Runnable runnable, long interval, long initialDelay) {
super(runnable);
this.initialDelay = initialDelay;
this.interval = interval;
this.initialDelay = initialDelay;
}
/**
@@ -59,10 +59,11 @@ public class IntervalTask extends Task {
public long getInterval() {
return interval;
return this.interval;
}
public long getInitialDelay() {
return initialDelay;
return this.initialDelay;
}
}

View File

@@ -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.
@@ -274,11 +274,19 @@ public class ScheduledTaskRegistrar implements InitializingBean, DisposableBean
(this.triggerTasks != null && !this.triggerTasks.isEmpty());
}
/**
* Calls {@link #scheduleTasks()} at bean construction time.
*/
public void afterPropertiesSet() {
scheduleTasks();
}
/**
* Schedule all registered tasks against the underlying {@linkplain
* #setTaskScheduler(TaskScheduler) task scheduler}.
*/
public void afterPropertiesSet() {
protected void scheduleTasks() {
long now = System.currentTimeMillis();
if (this.taskScheduler == null) {

View File

@@ -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.
@@ -38,8 +38,10 @@ import org.w3c.dom.NodeList;
public class ScheduledTasksBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
private static final String ELEMENT_SCHEDULED = "scheduled";
private static final long ZERO_INITIAL_DELAY = 0;
@Override
protected boolean shouldGenerateId() {
return true;
@@ -47,7 +49,7 @@ public class ScheduledTasksBeanDefinitionParser extends AbstractSingleBeanDefini
@Override
protected String getBeanClassName(Element element) {
return "org.springframework.scheduling.config.ScheduledTaskRegistrar";
return "org.springframework.scheduling.config.ContextLifecycleScheduledTaskRegistrar";
}
@Override

View File

@@ -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.
@@ -69,8 +69,21 @@ public class CronSequenceGenerator {
private final TimeZone timeZone;
/**
* Construct a {@link CronSequenceGenerator} from the pattern provided.
* Construct a {@link CronSequenceGenerator} from the pattern provided,
* using the default {@link TimeZone}.
* @param expression a space-separated list of time fields
* @throws IllegalArgumentException if the pattern cannot be parsed
* @see java.util.TimeZone#getDefault()
*/
public CronSequenceGenerator(String expression) {
this(expression, TimeZone.getDefault());
}
/**
* Construct a {@link CronSequenceGenerator} from the pattern provided,
* using the specified {@link TimeZone}.
* @param expression a space-separated list of time fields
* @param timeZone the TimeZone to use for generated trigger times
* @throws IllegalArgumentException if the pattern cannot be parsed
@@ -81,6 +94,7 @@ public class CronSequenceGenerator {
parse(expression);
}
/**
* Get the next {@link Date} in the sequence matching the Cron pattern and
* after the value provided. The return value will have a whole number of
@@ -112,12 +126,17 @@ public class CronSequenceGenerator {
calendar.setTimeZone(this.timeZone);
calendar.setTime(date);
// Truncate to the next whole second
calendar.add(Calendar.SECOND, 1);
// First, just reset the milliseconds and try to calculate from there...
calendar.set(Calendar.MILLISECOND, 0);
long originalTimestamp = calendar.getTimeInMillis();
doNext(calendar, calendar.get(Calendar.YEAR));
if (calendar.getTimeInMillis() == originalTimestamp) {
// We arrived at the original timestamp - round up to the next whole second and try again...
calendar.add(Calendar.SECOND, 1);
doNext(calendar, calendar.get(Calendar.YEAR));
}
return calendar.getTime();
}
@@ -135,7 +154,8 @@ public class CronSequenceGenerator {
int updateMinute = findNext(this.minutes, minute, calendar, Calendar.MINUTE, Calendar.HOUR_OF_DAY, resets);
if (minute == updateMinute) {
resets.add(Calendar.MINUTE);
} else {
}
else {
doNext(calendar, dot);
}
@@ -143,7 +163,8 @@ public class CronSequenceGenerator {
int updateHour = findNext(this.hours, hour, calendar, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_WEEK, resets);
if (hour == updateHour) {
resets.add(Calendar.HOUR_OF_DAY);
} else {
}
else {
doNext(calendar, dot);
}
@@ -152,7 +173,8 @@ public class CronSequenceGenerator {
int updateDayOfMonth = findNextDay(calendar, this.daysOfMonth, dayOfMonth, daysOfWeek, dayOfWeek, resets);
if (dayOfMonth == updateDayOfMonth) {
resets.add(Calendar.DAY_OF_MONTH);
} else {
}
else {
doNext(calendar, dot);
}
@@ -160,7 +182,8 @@ public class CronSequenceGenerator {
int updateMonth = findNext(this.months, month, calendar, Calendar.MONTH, Calendar.YEAR, resets);
if (month != updateMonth) {
if (calendar.get(Calendar.YEAR) - dot > 4) {
throw new IllegalStateException("Invalid cron expression led to runaway search for next trigger");
throw new IllegalArgumentException("Invalid cron expression \"" + this.expression +
"\" led to runaway search for next trigger");
}
doNext(calendar, dot);
}
@@ -181,7 +204,7 @@ public class CronSequenceGenerator {
reset(calendar, resets);
}
if (count >= max) {
throw new IllegalStateException("Overflow in day for expression=" + this.expression);
throw new IllegalArgumentException("Overflow in day for expression \"" + this.expression + "\"");
}
return dayOfMonth;
}
@@ -222,7 +245,8 @@ public class CronSequenceGenerator {
}
}
// Parsing logic invoked by the constructor.
// Parsing logic invoked by the constructor
/**
* Parse the given pattern expression.
@@ -230,8 +254,8 @@ public class CronSequenceGenerator {
private void parse(String expression) throws IllegalArgumentException {
String[] fields = StringUtils.tokenizeToStringArray(expression, " ");
if (fields.length != 6) {
throw new IllegalArgumentException(String.format(""
+ "cron expression must consist of 6 fields (found %d in %s)", fields.length, expression));
throw new IllegalArgumentException(String.format(
"Cron expression must consist of 6 fields (found %d in \"%s\")", fields.length, expression));
}
setNumberHits(this.seconds, fields[0], 0, 60);
setNumberHits(this.minutes, fields[1], 0, 60);
@@ -296,10 +320,12 @@ public class CronSequenceGenerator {
// Not an incrementer so it must be a range (possibly empty)
int[] range = getRange(field, min, max);
bits.set(range[0], range[1] + 1);
} else {
}
else {
String[] split = StringUtils.delimitedListToStringArray(field, "/");
if (split.length > 2) {
throw new IllegalArgumentException("Incrementer has more than two fields: " + field);
throw new IllegalArgumentException("Incrementer has more than two fields: '" +
field + "' in expression \"" + this.expression + "\"");
}
int[] range = getRange(split[0], min, max);
if (!split[0].contains("-")) {
@@ -322,19 +348,23 @@ public class CronSequenceGenerator {
}
if (!field.contains("-")) {
result[0] = result[1] = Integer.valueOf(field);
} else {
}
else {
String[] split = StringUtils.delimitedListToStringArray(field, "-");
if (split.length > 2) {
throw new IllegalArgumentException("Range has more than two fields: " + field);
throw new IllegalArgumentException("Range has more than two fields: '" +
field + "' in expression \"" + this.expression + "\"");
}
result[0] = Integer.valueOf(split[0]);
result[1] = Integer.valueOf(split[1]);
}
if (result[0] >= max || result[1] >= max) {
throw new IllegalArgumentException("Range exceeds maximum (" + max + "): " + field);
throw new IllegalArgumentException("Range exceeds maximum (" + max + "): '" +
field + "' in expression \"" + this.expression + "\"");
}
if (result[0] < min || result[1] < min) {
throw new IllegalArgumentException("Range less than minimum (" + min + "): " + field);
throw new IllegalArgumentException("Range less than minimum (" + min + "): '" +
field + "' in expression \"" + this.expression + "\"");
}
return result;
}

View File

@@ -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,12 +16,12 @@
package org.springframework.scheduling.support;
import java.util.Date;
import java.util.TimeZone;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import java.util.Date;
import java.util.TimeZone;
/**
* {@link Trigger} implementation for cron expressions.
* Wraps a {@link CronSequenceGenerator}.
@@ -41,7 +41,7 @@ public class CronTrigger implements Trigger {
* following cron expression conventions
*/
public CronTrigger(String cronExpression) {
this(cronExpression, TimeZone.getDefault());
this.sequenceGenerator = new CronSequenceGenerator(cronExpression);
}
/**
@@ -89,7 +89,7 @@ public class CronTrigger implements Trigger {
@Override
public String toString() {
return sequenceGenerator.toString();
return this.sequenceGenerator.toString();
}
}

View File

@@ -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.
@@ -31,8 +31,8 @@ import org.springframework.util.Assert;
* <emphasis>completion</emphasis> time). To measure the interval between the
* scheduled <emphasis>start</emphasis> time of each execution instead, set the
* 'fixedRate' property to {@code true}.
* <p>
* Note that the TaskScheduler interface already defines methods for scheduling
*
* <p>Note that the TaskScheduler interface already defines methods for scheduling
* tasks at fixed-rate or with fixed-delay. Both also support an optional value
* for the initial delay. Those methods should be used directly whenever
* possible. The value of this Trigger implementation is that it can be used
@@ -68,7 +68,7 @@ public class PeriodicTrigger implements Trigger {
*/
public PeriodicTrigger(long period, TimeUnit timeUnit) {
Assert.isTrue(period >= 0, "period must not be negative");
this.timeUnit = (timeUnit != null) ? timeUnit : TimeUnit.MILLISECONDS;
this.timeUnit = (timeUnit != null ? timeUnit : TimeUnit.MILLISECONDS);
this.period = this.timeUnit.toMillis(period);
}
@@ -91,6 +91,7 @@ public class PeriodicTrigger implements Trigger {
this.fixedRate = fixedRate;
}
/**
* Returns the time after which a task should run again.
*/
@@ -104,6 +105,7 @@ public class PeriodicTrigger implements Trigger {
return new Date(triggerContext.lastCompletionTime().getTime() + this.period);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -113,16 +115,12 @@ public class PeriodicTrigger implements Trigger {
return false;
}
PeriodicTrigger other = (PeriodicTrigger) obj;
return this.fixedRate == other.fixedRate
&& this.initialDelay == other.initialDelay
&& this.period == other.period;
return (this.fixedRate == other.fixedRate && this.initialDelay == other.initialDelay && this.period == other.period);
}
@Override
public int hashCode() {
return (this.fixedRate ? 17 : 29) +
(int) (37 * this.period) +
(int) (41 * this.initialDelay);
return (this.fixedRate ? 17 : 29) + (int) (37 * this.period) + (int) (41 * this.initialDelay);
}
}

View File

@@ -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.
@@ -26,6 +26,7 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionDefaults;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.xml.XmlReaderContext;
import org.springframework.scripting.support.ScriptFactoryPostProcessor;
@@ -64,6 +65,8 @@ class ScriptBeanDefinitionParser extends AbstractBeanDefinitionParser {
private static final String DEPENDENCY_CHECK_ATTRIBUTE = "dependency-check";
private static final String DEPENDS_ON_ATTRIBUTE = "depends-on";
private static final String INIT_METHOD_ATTRIBUTE = "init-method";
private static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";
@@ -138,6 +141,13 @@ class ScriptBeanDefinitionParser extends AbstractBeanDefinitionParser {
String dependencyCheck = element.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE);
bd.setDependencyCheck(parserContext.getDelegate().getDependencyCheck(dependencyCheck));
// Parse depends-on list of bean names.
String dependsOn = element.getAttribute(DEPENDS_ON_ATTRIBUTE);
if (StringUtils.hasLength(dependsOn)) {
bd.setDependsOn(StringUtils.tokenizeToStringArray(
dependsOn, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
// Retrieve the defaults for bean definitions within this parser context
BeanDefinitionDefaults beanDefinitionDefaults = parserContext.getDelegate().getBeanDefinitionDefaults();

View File

@@ -81,7 +81,7 @@ public abstract class JRubyScriptUtils {
Node scriptRootNode = ruby.parseEval(scriptSource, "", null, 0);
// keep using the deprecated runNormally variant for JRuby 1.1/1.2 compatibility...
IRubyObject rubyObject = ruby.runNormally(scriptRootNode, false);
IRubyObject rubyObject = ruby.runNormally(scriptRootNode, false);
if (rubyObject instanceof RubyNil) {
String className = findClassName(scriptRootNode);

View File

@@ -126,4 +126,4 @@ public class ResourceScriptSource implements ScriptSource {
public String toString() {
return this.resource.toString();
}
}
}

View File

@@ -18,7 +18,11 @@ package org.springframework.validation;
import java.beans.PropertyEditor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
@@ -141,7 +145,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
private BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor();
private Validator validator;
private final List<Validator> validators = new ArrayList<Validator>();
private ConversionService conversionService;
@@ -493,21 +497,58 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
/**
* Set the Validator to apply after each binding step.
* @see #addValidators(Validator...)
* @see #replaceValidators(Validator...)
*/
public void setValidator(Validator validator) {
if (validator != null && (getTarget() != null && !validator.supports(getTarget().getClass()))) {
throw new IllegalStateException("Invalid target for Validator [" + validator + "]: " + getTarget());
assertValidators(validator);
this.validators.clear();
this.validators.add(validator);
}
private void assertValidators(Validator... validators) {
Assert.notNull(validators, "Validators required");
for (Validator validator : validators) {
if (validator != null && (getTarget() != null && !validator.supports(getTarget().getClass()))) {
throw new IllegalStateException("Invalid target for Validator [" + validator + "]: " + getTarget());
}
}
this.validator = validator;
}
/**
* Return the Validator to apply after each binding step, if any.
* Add Validators to apply after each binding step.
* @see #setValidator(Validator)
* @see #replaceValidators(Validator...)
*/
public Validator getValidator() {
return this.validator;
public void addValidators(Validator... validators) {
assertValidators(validators);
this.validators.addAll(Arrays.asList(validators));
}
/**
* Replace the Validators to apply after each binding step.
* @see #setValidator(Validator)
* @see #addValidators(Validator...)
*/
public void replaceValidators(Validator... validators) {
assertValidators(validators);
this.validators.clear();
this.validators.addAll(Arrays.asList(validators));
}
/**
* Return the primary Validator to apply after each binding step, if any.
*/
public Validator getValidator() {
return this.validators.size() > 0 ? this.validators.get(0) : null;
}
/**
* Return the Validators to apply after data binding.
*/
public List<Validator> getValidators() {
return Collections.unmodifiableList(this.validators);
}
//---------------------------------------------------------------------
// Implementation of PropertyEditorRegistry/TypeConverter interface
@@ -708,28 +749,31 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
/**
* Invoke the specified Validator, if any.
* Invoke the specified Validators, if any.
* @see #setValidator(Validator)
* @see #getBindingResult()
*/
public void validate() {
this.validator.validate(getTarget(), getBindingResult());
for (Validator validator : this.validators) {
validator.validate(getTarget(), getBindingResult());
}
}
/**
* Invoke the specified Validator, if any, with the given validation hints.
* Invoke the specified Validators, if any, with the given validation hints.
* <p>Note: Validation hints may get ignored by the actual target Validator.
* @param validationHints one or more hint objects to be passed to a {@link SmartValidator}
* @see #setValidator(Validator)
* @see SmartValidator#validate(Object, Errors, Object...)
*/
public void validate(Object... validationHints) {
Validator validator = getValidator();
if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) {
((SmartValidator) validator).validate(getTarget(), getBindingResult(), validationHints);
}
else if (validator != null) {
validator.validate(getTarget(), getBindingResult());
for (Validator validator : getValidators()) {
if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) {
((SmartValidator) validator).validate(getTarget(), getBindingResult(), validationHints);
}
else if (validator != null) {
validator.validate(getTarget(), getBindingResult());
}
}
}

View File

@@ -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.
@@ -46,8 +46,8 @@ import org.springframework.validation.annotation.Validated;
* of that class. By default, JSR-303 will validate against its default group only.
*
* <p>As of Spring 3.1, this functionality requires Hibernate Validator 4.2 or higher.
* In Spring 3.1.2, this class will autodetect a Bean Validation 1.1 compliant provider
* and automatically use the standard method validation support there (once available).
* Once Bean Validation 1.1 becomes available, this class will autodetect a compliant
* provider and automatically use the standard method validation support there.
*
* @author Juergen Hoeller
* @since 3.1

View File

@@ -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.
@@ -99,7 +99,8 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
}
}
}
processConstraintViolations(this.targetValidator.validate(target, groups.toArray(new Class[groups.size()])), errors);
processConstraintViolations(
this.targetValidator.validate(target, groups.toArray(new Class[groups.size()])), errors);
}
/**
@@ -114,10 +115,11 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
FieldError fieldError = errors.getFieldError(field);
if (fieldError == null || !fieldError.isBindingFailure()) {
try {
String errorCode = violation.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName();
Object[] errorArgs = getArgumentsForConstraint(errors.getObjectName(), field, violation.getConstraintDescriptor());
ConstraintDescriptor<?> cd = violation.getConstraintDescriptor();
String errorCode = cd.getAnnotation().annotationType().getSimpleName();
Object[] errorArgs = getArgumentsForConstraint(errors.getObjectName(), field, cd);
if (errors instanceof BindingResult) {
// can do custom FieldError registration with invalid value from ConstraintViolation,
// Can do custom FieldError registration with invalid value from ConstraintViolation,
// as necessary for Hibernate Validator compatibility (non-indexed set path in field)
BindingResult bindingResult = (BindingResult) errors;
String nestedField = bindingResult.getNestedPath() + field;
@@ -128,8 +130,10 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
}
else {
Object invalidValue = violation.getInvalidValue();
if (!"".equals(field) && invalidValue == violation.getLeafBean()) {
// bean constraint with property path: retrieve the actual property value
if (!"".equals(field) && (invalidValue == violation.getLeafBean() ||
(field.contains(".") && !field.contains("[]")))) {
// Possibly a bean constraint with property path: retrieve the actual property value.
// However, explicitly avoid this for "address[]" style paths that we can't handle.
invalidValue = bindingResult.getRawFieldValue(field);
}
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode, field);