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:
@@ -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 "";
|
||||
}
|
||||
|
||||
@@ -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 "";
|
||||
}
|
||||
|
||||
@@ -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 {};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,4 +117,4 @@ class LazyParamAwareEvaluationContext extends StandardEvaluationContext {
|
||||
sb.append(m.toString());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -29,4 +29,4 @@ package org.springframework.context.annotation;
|
||||
public enum AdviceMode {
|
||||
PROXY,
|
||||
ASPECTJ
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -725,4 +725,4 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -110,4 +110,4 @@ public @interface EnableAspectJAutoProxy {
|
||||
*/
|
||||
boolean proxyTargetClass() default false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,4 +60,4 @@ public interface ImportBeanDefinitionRegistrar {
|
||||
public void registerBeanDefinitions(
|
||||
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,4 +44,4 @@ class PropertyOverrideBeanDefinitionParser extends AbstractPropertyLoadingBeanDe
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -195,7 +195,7 @@ public class FormattingConversionService extends GenericConversionService
|
||||
|
||||
private Class<? extends Annotation> annotationType;
|
||||
|
||||
private AnnotationFormatterFactory annotationFormatterFactory;
|
||||
private AnnotationFormatterFactory annotationFormatterFactory;
|
||||
|
||||
private Class<?> fieldType;
|
||||
|
||||
|
||||
@@ -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)
|
||||
*/
|
||||
|
||||
@@ -135,4 +135,4 @@ public class ReflectiveLoadTimeWeaver implements LoadTimeWeaver {
|
||||
return new SimpleThrowawayClassLoader(this.classLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -142,4 +142,4 @@ class JBossMCAdapter implements JBossClassLoaderAdapter {
|
||||
public ClassLoader getInstrumentableClassLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,4 +79,4 @@ class JBossMCTranslatorAdapter implements InvocationHandler {
|
||||
builder.append(this.transformer);
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,4 +68,4 @@ class JBossModulesAdapter implements JBossClassLoaderAdapter {
|
||||
public ClassLoader getInstrumentableClassLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,4 +85,4 @@ class OC4JClassLoaderAdapter {
|
||||
throw new IllegalStateException("Could not copy OC4J classloader", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,4 +92,4 @@ class OC4JClassPreprocessorAdapter implements InvocationHandler {
|
||||
builder.append(this.transformer);
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,5 +76,4 @@ public class OC4JLoadTimeWeaver implements LoadTimeWeaver {
|
||||
public ClassLoader getThrowawayClassLoader() {
|
||||
return this.classLoader.getThrowawayClassLoader();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -110,4 +110,4 @@ class WebLogicClassLoaderAdapter {
|
||||
throw new IllegalStateException("Could not construct WebLogic GenericClassLoader", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,4 +87,4 @@ class WebLogicClassPreProcessorAdapter implements InvocationHandler {
|
||||
builder.append(this.transformer);
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,4 +69,4 @@ public class WebLogicLoadTimeWeaver implements LoadTimeWeaver {
|
||||
public ClassLoader getThrowawayClassLoader() {
|
||||
return this.classLoader.getThrowawayClassLoader();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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() + "]";
|
||||
|
||||
@@ -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() + "]";
|
||||
|
||||
@@ -456,4 +456,4 @@ public class RmiServiceExporter extends RmiBasedExporter implements Initializing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 "";
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -126,4 +126,4 @@ public class ResourceScriptSource implements ScriptSource {
|
||||
public String toString() {
|
||||
return this.resource.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user