/* * Copyright 2009-2014 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.jdal.aop; import java.io.ObjectStreamException; import java.io.Serializable; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.annotation.Resource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValue; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanReference; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** * Serializable reference to a bean in application context. * * @author Jose Luis Martin * @since 2.0 */ public class SerializableReference implements Serializable { private static final Log log = LogFactory.getLog(SerializableReference.class); private static final Map<String, Object> serializedObjects = new ConcurrentHashMap<String, Object>(); /** force to use cglib in deserialized proxies */ private boolean proxyTargetClass; /** hold a copy of bean in memory instead looking in a bean factory */ private boolean useMemoryCache; /** dependency descriptor used to lookup bean on deserialization */ private DependencyDescriptor descriptor; /** bean factory to use */ private ConfigurableListableBeanFactory beanFactory; /** id for in memory cache */ private String id; /** bean name holding the serializable proxy */ private String beanName; /** target object */ private transient Object target; /** target bean name */ private String targetBeanName; public SerializableReference() { } public SerializableReference(Object target, boolean proxyTargetClass, boolean useMemoryCache, ConfigurableListableBeanFactory beanFactory, DependencyDescriptor descriptor, String beanName) { this.proxyTargetClass = proxyTargetClass; this.useMemoryCache = useMemoryCache; this.beanFactory = beanFactory; this.beanName = beanName; this.descriptor = descriptor; this.target = target; } public SerializableReference(Object target, boolean proxyTargetClass, boolean useMemoryCache, ConfigurableListableBeanFactory beanFactory, String targetBeanName) { this(target, proxyTargetClass, useMemoryCache, beanFactory, null, null); this.targetBeanName = targetBeanName; } public void serialize() { if (useMemoryCache) { this.id = ObjectUtils.identityToString(target); serializedObjects.put(id, target); if (log.isDebugEnabled()) log.debug("Added new serialized reference. serialized objects size [" + serializedObjects.size() + "]"); } } private Object readResolve() throws ObjectStreamException { if (beanFactory == null) { log.error("Can't not deserialize reference without bean factory"); return null; } if (useMemoryCache) { Object ret = serializedObjects.remove(this.id); if (ret != null) { if (log.isDebugEnabled()) log.debug("Removed a serialized reference. serialized objects size [" + serializedObjects.size() + "]"); return getSerializableProxy(ret); } } if (targetBeanName != null) { if (log.isDebugEnabled()) log.debug("Resolving serializable object to bean name [" + targetBeanName + "]"); return getSerializableProxy(); } if (log.isDebugEnabled()) log.debug("Resolving serializable object for [" + descriptor.getDependencyName() +"]"); Field field = descriptor.getField(); // Check bean definition BeanDefinition rbd = beanFactory.getBeanDefinition(beanName); PropertyValues pvs = rbd.getPropertyValues(); if (pvs.contains(field.getName())) { Object value = pvs.getPropertyValue(field.getName()); if (value instanceof BeanReference) { // cache the bean name this.targetBeanName = ((BeanReference) value).getBeanName(); return getSerializableProxy(); } } // Check Autowired try { Object bean = beanFactory.resolveDependency(descriptor, beanName); if (bean != null) return getSerializableProxy(bean); } catch(BeansException be) { // dependency not found. } // Check Resource annotation if (field.isAnnotationPresent(Resource.class)) { Resource r = field.getAnnotation(Resource.class); String name = StringUtils.isEmpty(r.name()) ? descriptor.getField().getName() : r.name(); if (beanFactory.containsBean(name)) { this.targetBeanName = name; return getSerializableProxy(); } } // Try with depend beans. String[] dependentBeans = beanFactory.getDependenciesForBean(beanName); List<String> candidates = new ArrayList<String>(); for (String name : dependentBeans) { if (beanFactory.isTypeMatch(name, descriptor.getDependencyType())); candidates.add(name); } if (candidates.size() == 1) return getSerializableProxy(beanFactory.getBean(candidates.get(0))); if (candidates.size() > 1) { for (PropertyValue pv : pvs.getPropertyValues()) { if (pv.getValue() instanceof BeanReference) { BeanReference br = (BeanReference) pv.getValue(); if (candidates.contains(br.getBeanName())) return getSerializableProxy(beanFactory.getBean(br.getBeanName())); } } } log.error("Cant not resolve serializable reference wiht candidates " + candidates.toArray()); return null; } protected Object getSerializableProxy(Object targetObject) { if (targetBeanName != null) return getSerializableProxy(); return SerializableProxyUtils.createSerializableProxy(targetObject, proxyTargetClass, useMemoryCache, beanFactory, descriptor, beanName); } protected Object getSerializableProxy() { return SerializableProxyUtils.createSerializableProxy(beanFactory.getBean(targetBeanName), proxyTargetClass, useMemoryCache, beanFactory, targetBeanName); } public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; } public void setTargetBeanName(String name) { this.targetBeanName = name; } public boolean isProxyTargetClass() { return proxyTargetClass; } public void setProxyTargetClass(boolean proxyTargetClass) { this.proxyTargetClass = proxyTargetClass; } public boolean isUseMemoryCache() { return useMemoryCache; } public void setUseMemoryCache(boolean useMemoryCache) { this.useMemoryCache = useMemoryCache; } public String getBeanName() { return beanName; } public void setBeanName(String beanName) { this.beanName = beanName; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } public String getTargetBeanName() { return targetBeanName; } public ConfigurableListableBeanFactory getBeanFactory() { return beanFactory; } public void setBeanFactory(ConfigurableListableBeanFactory beanFactory) { this.beanFactory = beanFactory; } }