Fix line endings
This commit is contained in:
@@ -1,409 +1,409 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.cloud.context.scope;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.Scope;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.cloud.context.config.BeanLifecycleDecorator;
|
||||
import org.springframework.cloud.context.config.BeanLifecycleDecorator.Context;
|
||||
import org.springframework.cloud.context.config.StandardBeanLifecycleDecorator;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A generic Scope implementation.
|
||||
* </p>
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
*/
|
||||
public class GenericScope implements Scope, BeanFactoryPostProcessor, DisposableBean {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(GenericScope.class);
|
||||
|
||||
public static final String SCOPED_TARGET_PREFIX = "scopedTarget.";
|
||||
|
||||
private BeanLifecycleWrapperCache cache = new BeanLifecycleWrapperCache(
|
||||
new StandardScopeCache());
|
||||
|
||||
private String name = "generic";
|
||||
|
||||
private boolean proxyTargetClass = true;
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private StandardEvaluationContext evaluationContext;
|
||||
|
||||
private String id;
|
||||
|
||||
private BeanLifecycleDecorator<?> lifecycle;
|
||||
|
||||
private Map<String, Exception> errors = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Manual override for the serialization id that will be used to identify the bean
|
||||
* factory. The default is a unique key based on the bean names in the bean factory.
|
||||
*
|
||||
* @param id the id to set
|
||||
*/
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of this scope. Default "generic".
|
||||
*
|
||||
* @param name the name value to set
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate that proxies should be created for the concrete type, not just the
|
||||
* interfaces, of the scoped beans.
|
||||
*
|
||||
* @param proxyTargetClass the flag value to set
|
||||
*/
|
||||
public void setProxyTargetClass(boolean proxyTargetClass) {
|
||||
this.proxyTargetClass = proxyTargetClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* The cache implementation to use for bean instances in this scope.
|
||||
*
|
||||
* @param cache the cache to use
|
||||
*/
|
||||
public void setScopeCache(ScopeCache cache) {
|
||||
this.cache = new BeanLifecycleWrapperCache(cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to manage the creation and destruction of beans.
|
||||
*
|
||||
* @param lifecycle the bean lifecycle to set
|
||||
*/
|
||||
public void setBeanLifecycleManager(BeanLifecycleDecorator<?> lifecycle) {
|
||||
this.lifecycle = lifecycle;
|
||||
}
|
||||
|
||||
/**
|
||||
* A map of bean name to errors when instantiating the bean.
|
||||
*
|
||||
* @return the errors accumulated since the latest destroy
|
||||
*/
|
||||
public Map<String, Exception> getErrors() {
|
||||
return this.errors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
List<Throwable> errors = new ArrayList<Throwable>();
|
||||
Collection<BeanLifecycleWrapper> wrappers = this.cache.clear();
|
||||
for (BeanLifecycleWrapper wrapper : wrappers) {
|
||||
try {
|
||||
wrapper.destroy();
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
errors.add(e);
|
||||
}
|
||||
}
|
||||
if (!errors.isEmpty()) {
|
||||
throw wrapIfNecessary(errors.get(0));
|
||||
}
|
||||
this.errors.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the named bean (i.e. flush it from the cache by default).
|
||||
*
|
||||
* @param name the bean name to flush
|
||||
* @return true if the bean was already cached, false otherwise
|
||||
*/
|
||||
protected boolean destroy(String name) {
|
||||
BeanLifecycleWrapper wrapper = this.cache.remove(name);
|
||||
if (wrapper != null) {
|
||||
wrapper.destroy();
|
||||
this.errors.remove(name);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(String name, ObjectFactory<?> objectFactory) {
|
||||
if (this.lifecycle == null) {
|
||||
this.lifecycle = new StandardBeanLifecycleDecorator(this.proxyTargetClass);
|
||||
}
|
||||
BeanLifecycleWrapper value = this.cache.put(name,
|
||||
new BeanLifecycleWrapper(name, objectFactory, this.lifecycle));
|
||||
try {
|
||||
return value.getBean();
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
this.errors.put(name, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConversationId() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDestructionCallback(String name, Runnable callback) {
|
||||
BeanLifecycleWrapper value = this.cache.get(name);
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
value.setDestroyCallback(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object remove(String name) {
|
||||
BeanLifecycleWrapper value = this.cache.remove(name);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
// Someone might have added another object with the same key, but we
|
||||
// keep the method contract by removing the
|
||||
// value we found anyway
|
||||
return value.getBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveContextualObject(String key) {
|
||||
Expression expression = parseExpression(key);
|
||||
return expression.getValue(this.evaluationContext, this.beanFactory);
|
||||
}
|
||||
|
||||
private Expression parseExpression(String input) {
|
||||
if (StringUtils.hasText(input)) {
|
||||
ExpressionParser parser = new SpelExpressionParser();
|
||||
try {
|
||||
return parser.parseExpression(input);
|
||||
}
|
||||
catch (ParseException e) {
|
||||
throw new IllegalArgumentException("Cannot parse expression: " + input,
|
||||
e);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
|
||||
throws BeansException {
|
||||
beanFactory.registerScope(this.name, this);
|
||||
setSerializationId(beanFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the bean factory is a DefaultListableBeanFactory then it can serialize scoped
|
||||
* beans and deserialize them in another context (even in another JVM), as long as the
|
||||
* ids of the bean factories match. This method sets up the serialization id to be
|
||||
* either the id provided to the scope instance, or if that is null, a hash of all the
|
||||
* bean names.
|
||||
*
|
||||
* @param beanFactory the bean factory to configure
|
||||
*/
|
||||
private void setSerializationId(ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
if (beanFactory instanceof DefaultListableBeanFactory) {
|
||||
|
||||
String id = this.id;
|
||||
if (id == null) {
|
||||
List<String> list = new ArrayList<>(
|
||||
Arrays.asList(beanFactory.getBeanDefinitionNames()));
|
||||
Collections.sort(list);
|
||||
String names = list.toString();
|
||||
logger.debug("Generating bean factory id from names: " + names);
|
||||
id = UUID.nameUUIDFromBytes(names.getBytes()).toString();
|
||||
}
|
||||
|
||||
logger.info("BeanFactory id=" + id);
|
||||
((DefaultListableBeanFactory) beanFactory).setSerializationId(id);
|
||||
|
||||
}
|
||||
else {
|
||||
logger.warn(
|
||||
"BeanFactory was not a DefaultListableBeanFactory, scoped proxy beans "
|
||||
+ "cannot be serialized.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static RuntimeException wrapIfNecessary(Throwable throwable) {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
return (RuntimeException) throwable;
|
||||
}
|
||||
if (throwable instanceof Error) {
|
||||
throw (Error) throwable;
|
||||
}
|
||||
return new IllegalStateException(throwable);
|
||||
}
|
||||
|
||||
protected String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
private static class BeanLifecycleWrapperCache {
|
||||
|
||||
private final ScopeCache cache;
|
||||
|
||||
public BeanLifecycleWrapperCache(ScopeCache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public BeanLifecycleWrapper remove(String name) {
|
||||
return (BeanLifecycleWrapper) this.cache.remove(name);
|
||||
}
|
||||
|
||||
public Collection<BeanLifecycleWrapper> clear() {
|
||||
Collection<Object> values = this.cache.clear();
|
||||
Collection<BeanLifecycleWrapper> wrappers = new LinkedHashSet<BeanLifecycleWrapper>();
|
||||
for (Object object : values) {
|
||||
wrappers.add((BeanLifecycleWrapper) object);
|
||||
}
|
||||
return wrappers;
|
||||
}
|
||||
|
||||
public BeanLifecycleWrapper get(String name) {
|
||||
return (BeanLifecycleWrapper) this.cache.get(name);
|
||||
}
|
||||
|
||||
public BeanLifecycleWrapper put(String name, BeanLifecycleWrapper value) {
|
||||
return (BeanLifecycleWrapper) this.cache.put(name, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for a bean instance and any destruction callback (DisposableBean etc.) that
|
||||
* is registered for it. Also decorates the bean to optionally guard it from
|
||||
* concurrent access (for instance).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
private static class BeanLifecycleWrapper {
|
||||
|
||||
private Object bean;
|
||||
|
||||
private Context<?> context;
|
||||
|
||||
private final String name;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private final BeanLifecycleDecorator lifecycle;
|
||||
|
||||
private final ObjectFactory<?> objectFactory;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public BeanLifecycleWrapper(String name, ObjectFactory<?> objectFactory,
|
||||
BeanLifecycleDecorator lifecycle) {
|
||||
this.name = name;
|
||||
this.objectFactory = objectFactory;
|
||||
this.lifecycle = lifecycle;
|
||||
}
|
||||
|
||||
public void setDestroyCallback(Runnable callback) {
|
||||
this.context = this.lifecycle.decorateDestructionCallback(callback);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object getBean() {
|
||||
if (this.bean == null) {
|
||||
synchronized (this.name) {
|
||||
if (this.bean == null) {
|
||||
this.bean = this.lifecycle.decorateBean(
|
||||
this.objectFactory.getObject(), this.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.bean;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (this.context == null) {
|
||||
return;
|
||||
}
|
||||
Runnable callback = this.context.getCallback();
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
BeanLifecycleWrapper other = (BeanLifecycleWrapper) obj;
|
||||
if (this.name == null) {
|
||||
if (other.name != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!this.name.equals(other.name)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2002-2009 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.cloud.context.scope;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.Scope;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.cloud.context.config.BeanLifecycleDecorator;
|
||||
import org.springframework.cloud.context.config.BeanLifecycleDecorator.Context;
|
||||
import org.springframework.cloud.context.config.StandardBeanLifecycleDecorator;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A generic Scope implementation.
|
||||
* </p>
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
*/
|
||||
public class GenericScope implements Scope, BeanFactoryPostProcessor, DisposableBean {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(GenericScope.class);
|
||||
|
||||
public static final String SCOPED_TARGET_PREFIX = "scopedTarget.";
|
||||
|
||||
private BeanLifecycleWrapperCache cache = new BeanLifecycleWrapperCache(
|
||||
new StandardScopeCache());
|
||||
|
||||
private String name = "generic";
|
||||
|
||||
private boolean proxyTargetClass = true;
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private StandardEvaluationContext evaluationContext;
|
||||
|
||||
private String id;
|
||||
|
||||
private BeanLifecycleDecorator<?> lifecycle;
|
||||
|
||||
private Map<String, Exception> errors = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Manual override for the serialization id that will be used to identify the bean
|
||||
* factory. The default is a unique key based on the bean names in the bean factory.
|
||||
*
|
||||
* @param id the id to set
|
||||
*/
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of this scope. Default "generic".
|
||||
*
|
||||
* @param name the name value to set
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate that proxies should be created for the concrete type, not just the
|
||||
* interfaces, of the scoped beans.
|
||||
*
|
||||
* @param proxyTargetClass the flag value to set
|
||||
*/
|
||||
public void setProxyTargetClass(boolean proxyTargetClass) {
|
||||
this.proxyTargetClass = proxyTargetClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* The cache implementation to use for bean instances in this scope.
|
||||
*
|
||||
* @param cache the cache to use
|
||||
*/
|
||||
public void setScopeCache(ScopeCache cache) {
|
||||
this.cache = new BeanLifecycleWrapperCache(cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to manage the creation and destruction of beans.
|
||||
*
|
||||
* @param lifecycle the bean lifecycle to set
|
||||
*/
|
||||
public void setBeanLifecycleManager(BeanLifecycleDecorator<?> lifecycle) {
|
||||
this.lifecycle = lifecycle;
|
||||
}
|
||||
|
||||
/**
|
||||
* A map of bean name to errors when instantiating the bean.
|
||||
*
|
||||
* @return the errors accumulated since the latest destroy
|
||||
*/
|
||||
public Map<String, Exception> getErrors() {
|
||||
return this.errors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
List<Throwable> errors = new ArrayList<Throwable>();
|
||||
Collection<BeanLifecycleWrapper> wrappers = this.cache.clear();
|
||||
for (BeanLifecycleWrapper wrapper : wrappers) {
|
||||
try {
|
||||
wrapper.destroy();
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
errors.add(e);
|
||||
}
|
||||
}
|
||||
if (!errors.isEmpty()) {
|
||||
throw wrapIfNecessary(errors.get(0));
|
||||
}
|
||||
this.errors.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the named bean (i.e. flush it from the cache by default).
|
||||
*
|
||||
* @param name the bean name to flush
|
||||
* @return true if the bean was already cached, false otherwise
|
||||
*/
|
||||
protected boolean destroy(String name) {
|
||||
BeanLifecycleWrapper wrapper = this.cache.remove(name);
|
||||
if (wrapper != null) {
|
||||
wrapper.destroy();
|
||||
this.errors.remove(name);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(String name, ObjectFactory<?> objectFactory) {
|
||||
if (this.lifecycle == null) {
|
||||
this.lifecycle = new StandardBeanLifecycleDecorator(this.proxyTargetClass);
|
||||
}
|
||||
BeanLifecycleWrapper value = this.cache.put(name,
|
||||
new BeanLifecycleWrapper(name, objectFactory, this.lifecycle));
|
||||
try {
|
||||
return value.getBean();
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
this.errors.put(name, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConversationId() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDestructionCallback(String name, Runnable callback) {
|
||||
BeanLifecycleWrapper value = this.cache.get(name);
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
value.setDestroyCallback(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object remove(String name) {
|
||||
BeanLifecycleWrapper value = this.cache.remove(name);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
// Someone might have added another object with the same key, but we
|
||||
// keep the method contract by removing the
|
||||
// value we found anyway
|
||||
return value.getBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveContextualObject(String key) {
|
||||
Expression expression = parseExpression(key);
|
||||
return expression.getValue(this.evaluationContext, this.beanFactory);
|
||||
}
|
||||
|
||||
private Expression parseExpression(String input) {
|
||||
if (StringUtils.hasText(input)) {
|
||||
ExpressionParser parser = new SpelExpressionParser();
|
||||
try {
|
||||
return parser.parseExpression(input);
|
||||
}
|
||||
catch (ParseException e) {
|
||||
throw new IllegalArgumentException("Cannot parse expression: " + input,
|
||||
e);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
|
||||
throws BeansException {
|
||||
beanFactory.registerScope(this.name, this);
|
||||
setSerializationId(beanFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the bean factory is a DefaultListableBeanFactory then it can serialize scoped
|
||||
* beans and deserialize them in another context (even in another JVM), as long as the
|
||||
* ids of the bean factories match. This method sets up the serialization id to be
|
||||
* either the id provided to the scope instance, or if that is null, a hash of all the
|
||||
* bean names.
|
||||
*
|
||||
* @param beanFactory the bean factory to configure
|
||||
*/
|
||||
private void setSerializationId(ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
if (beanFactory instanceof DefaultListableBeanFactory) {
|
||||
|
||||
String id = this.id;
|
||||
if (id == null) {
|
||||
List<String> list = new ArrayList<>(
|
||||
Arrays.asList(beanFactory.getBeanDefinitionNames()));
|
||||
Collections.sort(list);
|
||||
String names = list.toString();
|
||||
logger.debug("Generating bean factory id from names: " + names);
|
||||
id = UUID.nameUUIDFromBytes(names.getBytes()).toString();
|
||||
}
|
||||
|
||||
logger.info("BeanFactory id=" + id);
|
||||
((DefaultListableBeanFactory) beanFactory).setSerializationId(id);
|
||||
|
||||
}
|
||||
else {
|
||||
logger.warn(
|
||||
"BeanFactory was not a DefaultListableBeanFactory, scoped proxy beans "
|
||||
+ "cannot be serialized.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static RuntimeException wrapIfNecessary(Throwable throwable) {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
return (RuntimeException) throwable;
|
||||
}
|
||||
if (throwable instanceof Error) {
|
||||
throw (Error) throwable;
|
||||
}
|
||||
return new IllegalStateException(throwable);
|
||||
}
|
||||
|
||||
protected String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
private static class BeanLifecycleWrapperCache {
|
||||
|
||||
private final ScopeCache cache;
|
||||
|
||||
public BeanLifecycleWrapperCache(ScopeCache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public BeanLifecycleWrapper remove(String name) {
|
||||
return (BeanLifecycleWrapper) this.cache.remove(name);
|
||||
}
|
||||
|
||||
public Collection<BeanLifecycleWrapper> clear() {
|
||||
Collection<Object> values = this.cache.clear();
|
||||
Collection<BeanLifecycleWrapper> wrappers = new LinkedHashSet<BeanLifecycleWrapper>();
|
||||
for (Object object : values) {
|
||||
wrappers.add((BeanLifecycleWrapper) object);
|
||||
}
|
||||
return wrappers;
|
||||
}
|
||||
|
||||
public BeanLifecycleWrapper get(String name) {
|
||||
return (BeanLifecycleWrapper) this.cache.get(name);
|
||||
}
|
||||
|
||||
public BeanLifecycleWrapper put(String name, BeanLifecycleWrapper value) {
|
||||
return (BeanLifecycleWrapper) this.cache.put(name, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for a bean instance and any destruction callback (DisposableBean etc.) that
|
||||
* is registered for it. Also decorates the bean to optionally guard it from
|
||||
* concurrent access (for instance).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
private static class BeanLifecycleWrapper {
|
||||
|
||||
private Object bean;
|
||||
|
||||
private Context<?> context;
|
||||
|
||||
private final String name;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private final BeanLifecycleDecorator lifecycle;
|
||||
|
||||
private final ObjectFactory<?> objectFactory;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public BeanLifecycleWrapper(String name, ObjectFactory<?> objectFactory,
|
||||
BeanLifecycleDecorator lifecycle) {
|
||||
this.name = name;
|
||||
this.objectFactory = objectFactory;
|
||||
this.lifecycle = lifecycle;
|
||||
}
|
||||
|
||||
public void setDestroyCallback(Runnable callback) {
|
||||
this.context = this.lifecycle.decorateDestructionCallback(callback);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object getBean() {
|
||||
if (this.bean == null) {
|
||||
synchronized (this.name) {
|
||||
if (this.bean == null) {
|
||||
this.bean = this.lifecycle.decorateBean(
|
||||
this.objectFactory.getObject(), this.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.bean;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (this.context == null) {
|
||||
return;
|
||||
}
|
||||
Runnable callback = this.context.getCallback();
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
BeanLifecycleWrapper other = (BeanLifecycleWrapper) obj;
|
||||
if (this.name == null) {
|
||||
if (other.name != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!this.name.equals(other.name)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,153 +1,153 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.cloud.context.scope.refresh;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.cloud.context.scope.GenericScope;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A Scope implementation that allows for beans to be refreshed dynamically at runtime
|
||||
* (see {@link #refresh(String)} and {@link #refreshAll()}). If a bean is refreshed then
|
||||
* the next time the bean is accessed (i.e. a method is executed) a new instance is
|
||||
* created. All lifecycle methods are applied to the bean instances, so any destruction
|
||||
* callbacks that were registered in the bean factory are called when it is refreshed, and
|
||||
* then the initialization callbacks are invoked as normal when the new instance is
|
||||
* created. A new bean instance is created from the original bean definition, so any
|
||||
* externalized content (property placeholders or expressions in string literals) is
|
||||
* re-evaluated when it is created.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Note that all beans in this scope are <em>only</em> initialized when first accessed, so
|
||||
* the scope forces lazy initialization semantics. The implementation involves creating a
|
||||
* proxy for every bean in the scope, so there is a flag
|
||||
* {@link #setProxyTargetClass(boolean) proxyTargetClass} which controls the proxy
|
||||
* creation, defaulting to JDK dynamic proxies and therefore only exposing the interfaces
|
||||
* implemented by a bean. If callers need access to other methods then the flag needs to
|
||||
* be set (and CGLib present on the classpath). Because this scope automatically proxies
|
||||
* all its beans, there is no need to add <code><aop:auto-proxy/></code> to any bean
|
||||
* definitions.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The scoped proxy approach adopted here has a side benefit that bean instances are
|
||||
* automatically {@link Serializable}, and can be sent across the wire as long as the
|
||||
* receiver has an identical application context on the other side. To ensure that the two
|
||||
* contexts agree that they are identical they have to have the same serialization id. One
|
||||
* will be generated automatically by default from the bean names, so two contexts with
|
||||
* the same bean names are by default able to exchange beans by name. If you need to
|
||||
* override the default id then provide an explicit {@link #setId(String) id} when the
|
||||
* Scope is declared.
|
||||
* </p>
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
*/
|
||||
@ManagedResource
|
||||
public class RefreshScope extends GenericScope
|
||||
implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor, Ordered {
|
||||
|
||||
private ApplicationContext context;
|
||||
private BeanDefinitionRegistry registry;
|
||||
private boolean eager = true;
|
||||
private int order = Ordered.LOWEST_PRECEDENCE - 100;
|
||||
|
||||
/**
|
||||
* Create a scope instance and give it the default name: "refresh".
|
||||
*/
|
||||
public RefreshScope() {
|
||||
super.setName("refresh");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to determine whether all beans in refresh scope should be instantiated eagerly
|
||||
* on startup. Default true.
|
||||
*
|
||||
* @param eager the flag to set
|
||||
*/
|
||||
public void setEager(boolean eager) {
|
||||
this.eager = eager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
|
||||
throws BeansException {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void start(ContextRefreshedEvent event) {
|
||||
if (event.getApplicationContext() == this.context) {
|
||||
if (this.eager && this.registry != null) {
|
||||
for (String name : this.context.getBeanDefinitionNames()) {
|
||||
BeanDefinition definition = this.registry.getBeanDefinition(name);
|
||||
if (this.getName().equals(definition.getScope())
|
||||
&& !definition.isLazyInit()) {
|
||||
this.context.getBean(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ManagedOperation(description = "Dispose of the current instance of bean name provided and force a refresh on next method execution.")
|
||||
public boolean refresh(String name) {
|
||||
if (!name.startsWith(SCOPED_TARGET_PREFIX)) {
|
||||
// User wants to refresh the bean with this name but that isn't the one in the
|
||||
// cache...
|
||||
name = SCOPED_TARGET_PREFIX + name;
|
||||
}
|
||||
// Ensure lifecycle is finished if bean was disposable
|
||||
if (super.destroy(name)) {
|
||||
this.context.publishEvent(new RefreshScopeRefreshedEvent(name));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ManagedOperation(description = "Dispose of the current instance of all beans in this scope and force a refresh on next method execution.")
|
||||
public void refreshAll() {
|
||||
super.destroy();
|
||||
this.context.publishEvent(new RefreshScopeRefreshedEvent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.context = context;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright 2002-2009 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.cloud.context.scope.refresh;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.cloud.context.scope.GenericScope;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A Scope implementation that allows for beans to be refreshed dynamically at runtime
|
||||
* (see {@link #refresh(String)} and {@link #refreshAll()}). If a bean is refreshed then
|
||||
* the next time the bean is accessed (i.e. a method is executed) a new instance is
|
||||
* created. All lifecycle methods are applied to the bean instances, so any destruction
|
||||
* callbacks that were registered in the bean factory are called when it is refreshed, and
|
||||
* then the initialization callbacks are invoked as normal when the new instance is
|
||||
* created. A new bean instance is created from the original bean definition, so any
|
||||
* externalized content (property placeholders or expressions in string literals) is
|
||||
* re-evaluated when it is created.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Note that all beans in this scope are <em>only</em> initialized when first accessed, so
|
||||
* the scope forces lazy initialization semantics. The implementation involves creating a
|
||||
* proxy for every bean in the scope, so there is a flag
|
||||
* {@link #setProxyTargetClass(boolean) proxyTargetClass} which controls the proxy
|
||||
* creation, defaulting to JDK dynamic proxies and therefore only exposing the interfaces
|
||||
* implemented by a bean. If callers need access to other methods then the flag needs to
|
||||
* be set (and CGLib present on the classpath). Because this scope automatically proxies
|
||||
* all its beans, there is no need to add <code><aop:auto-proxy/></code> to any bean
|
||||
* definitions.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The scoped proxy approach adopted here has a side benefit that bean instances are
|
||||
* automatically {@link Serializable}, and can be sent across the wire as long as the
|
||||
* receiver has an identical application context on the other side. To ensure that the two
|
||||
* contexts agree that they are identical they have to have the same serialization id. One
|
||||
* will be generated automatically by default from the bean names, so two contexts with
|
||||
* the same bean names are by default able to exchange beans by name. If you need to
|
||||
* override the default id then provide an explicit {@link #setId(String) id} when the
|
||||
* Scope is declared.
|
||||
* </p>
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
*/
|
||||
@ManagedResource
|
||||
public class RefreshScope extends GenericScope
|
||||
implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor, Ordered {
|
||||
|
||||
private ApplicationContext context;
|
||||
private BeanDefinitionRegistry registry;
|
||||
private boolean eager = true;
|
||||
private int order = Ordered.LOWEST_PRECEDENCE - 100;
|
||||
|
||||
/**
|
||||
* Create a scope instance and give it the default name: "refresh".
|
||||
*/
|
||||
public RefreshScope() {
|
||||
super.setName("refresh");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to determine whether all beans in refresh scope should be instantiated eagerly
|
||||
* on startup. Default true.
|
||||
*
|
||||
* @param eager the flag to set
|
||||
*/
|
||||
public void setEager(boolean eager) {
|
||||
this.eager = eager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
|
||||
throws BeansException {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void start(ContextRefreshedEvent event) {
|
||||
if (event.getApplicationContext() == this.context) {
|
||||
if (this.eager && this.registry != null) {
|
||||
for (String name : this.context.getBeanDefinitionNames()) {
|
||||
BeanDefinition definition = this.registry.getBeanDefinition(name);
|
||||
if (this.getName().equals(definition.getScope())
|
||||
&& !definition.isLazyInit()) {
|
||||
this.context.getBean(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ManagedOperation(description = "Dispose of the current instance of bean name provided and force a refresh on next method execution.")
|
||||
public boolean refresh(String name) {
|
||||
if (!name.startsWith(SCOPED_TARGET_PREFIX)) {
|
||||
// User wants to refresh the bean with this name but that isn't the one in the
|
||||
// cache...
|
||||
name = SCOPED_TARGET_PREFIX + name;
|
||||
}
|
||||
// Ensure lifecycle is finished if bean was disposable
|
||||
if (super.destroy(name)) {
|
||||
this.context.publishEvent(new RefreshScopeRefreshedEvent(name));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ManagedOperation(description = "Dispose of the current instance of all beans in this scope and force a refresh on next method execution.")
|
||||
public void refreshAll() {
|
||||
super.destroy();
|
||||
this.context.publishEvent(new RefreshScopeRefreshedEvent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.context = context;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.cloud.context.scope.thread;
|
||||
|
||||
import org.springframework.cloud.context.scope.GenericScope;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
*/
|
||||
public class ThreadScope extends GenericScope {
|
||||
|
||||
/**
|
||||
* Create a scope instance and give it the default name: "thread".
|
||||
*/
|
||||
public ThreadScope() {
|
||||
super();
|
||||
super.setName("thread");
|
||||
super.setScopeCache(new ThreadLocalScopeCache());
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2002-2009 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.cloud.context.scope.thread;
|
||||
|
||||
import org.springframework.cloud.context.scope.GenericScope;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
*/
|
||||
public class ThreadScope extends GenericScope {
|
||||
|
||||
/**
|
||||
* Create a scope instance and give it the default name: "thread".
|
||||
*/
|
||||
public ThreadScope() {
|
||||
super();
|
||||
super.setName("thread");
|
||||
super.setScopeCache(new ThreadLocalScopeCache());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user