package jfun.yan.spring;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import jfun.yan.Component;
import jfun.yan.Container;
import jfun.yan.util.ReflectionUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.HierarchicalMessageSource;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
/**
* This class adapts a yan Container object
* to Spring's ApplicationContext (primarily ListableBeanFactory) interface.
* <p>
* Many Thanks to spring for making ApplicationContext an interface!
* It helped.
* </p>
* <p>
* @author Ben Yu
* Nov 16, 2005 11:40:13 PM
*/
public class Container2ApplicationContext implements ListableBeanFactory,
ApplicationContext{
private final String name;
private final long startup_timestamp;
private final Container yan;
private final MessageSource parent_message_source;
private final ResourcePatternResolver resource_loader;
private final ApplicationEventPublisher parent_publisher;
private static final String prefix = BeanFactory.FACTORY_BEAN_PREFIX;;
private MessageSource message_source = null;
private ApplicationEventMulticaster multicaster = null;
private synchronized MessageSource getMessageSource(){
if(message_source!=null) return message_source;
final String key = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME;
final Class type = yan.getComponentType(key);
if(type!=null && MessageSource.class.isAssignableFrom(type)){
message_source = (MessageSource)yan.getInstance(key);
if (parent_message_source != null && message_source instanceof HierarchicalMessageSource) {
HierarchicalMessageSource hms = (HierarchicalMessageSource) message_source;
if (hms.getParentMessageSource() == null) {
// Only set parent context as parent MessageSource if no parent MessageSource
// registered already.
hms.setParentMessageSource(parent_message_source);
}
}
}
else{
return parent_message_source;
}
return message_source;
}
private synchronized ApplicationEventMulticaster getApplicationEventMulticaster(){
if(multicaster!=null) return multicaster;
final String key = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME;
final Class type = yan.getComponentType(key);
if(type!=null && ApplicationEventMulticaster.class.isAssignableFrom(type)){
multicaster = (ApplicationEventMulticaster)yan.getInstance(key);
}
else{
multicaster = new SimpleApplicationEventMulticaster();
}
return multicaster;
}
/**
* Create a Container2BeanFactory object.
* @param message_source the MessageSource object.
* @param resource_loader the ResourcePatternResolver object.
* @param publisher the ApplicationEventPublisher object.
* @param name the name of the ApplicationContext.
* @param startup_timestamp the startup timestamp.
* @param yan the yan Container object.
*/
public Container2ApplicationContext(
MessageSource message_source, ResourcePatternResolver resource_loader,
ApplicationEventPublisher publisher,
String name, long startup_timestamp,
Container yan
)
{
this.parent_message_source = message_source;
this.name = name;
this.resource_loader = resource_loader;
this.startup_timestamp = startup_timestamp;
this.yan = yan;
this.parent_publisher = publisher;
}
/**
* Get the container in this object.
*/
public Container getContainer() {
return yan;
}
/**
* Get the number of components registered under a string key.
*/
public int getBeanDefinitionCount() {
final Set keys = yan.keys();
int ret = 0;
for(Iterator it=keys.iterator(); it.hasNext();){
final Object key = it.next();
if(key instanceof String){
ret++;
}
}
return ret;
}
/**
* Get all the component keys that are of String type.
*/
public String[] getBeanDefinitionNames() {
final Set keys = yan.keys();
final ArrayList names = new ArrayList();
for(Iterator it=keys.iterator(); it.hasNext();){
final Object key = it.next();
if(key instanceof String){
names.add((String)key);
}
}
final String[] ret = new String[names.size()];
names.toArray(ret);
return ret;
}
/**
* retrieves the string keys of all components
* whose's static instance type is a subtype of the required type.
* <p>
* FactoryBean's are not instantiated.
* </p>
* @param type the type to match. Null indicates all.
* @return the keys.
*/
public String[] getBeanDefinitionNames(Class type) {
return getBeanNamesForType(type, true, false);
}
/**
* retrieves the string keys of all components
* whose's static instance type is a subtype of the required type.
* <p>
* FactoryBean's are instantiated if necessary to find out the product type.
* </p>
* @param type the type to match. Null indicates all.
* @return the keys.
*/
public String[] getBeanNamesForType(Class type){
return getBeanNamesForType(type, true, true);
}
private interface BeanListener{
/**
* When a regular bean is found.
*/
void onBean(String key, Component c);
/**
* When a FactoryBean is instantiated and the product type and singleton mode satisifies the condition.
*/
void onFactoryBean(String key, FactoryBean fb);
/**
* When a FactoryBean component itself is matched.
*/
void onFactoryBean(String key, Component fbc);
}
/**
* Iterate through the beans for a certain type.
* @param type the required type.
* @param includePrototypes whether prototype beans are included.
* (thread scope singleotn is considered prototype as well because it doesn't guarantee one instance globally).
* @param includeFactoryBeans whether we want to instantiate FactoryBean to find out the product type
* and the singleton mode.
* @param listener the BeanListener object.
*/
private void foreachBean(Class type,
boolean includePrototypes, boolean includeFactoryBeans,
BeanListener listener){
final boolean for_factorybean = isFactoryBeanType(type);
for(Iterator it=yan.keys().iterator();it.hasNext();){
final Object key = it.next();
if(!(key instanceof String)) continue;
final jfun.yan.Component c = yan.getComponent(key);
final Component fbc = SpringAdapter.getFactoryBeanComponent(c);
if(fbc!=null){
//I am Factory Bean
if(includeFactoryBeans){
//attempt to match the FactoryBean result.
final FactoryBean fb = (FactoryBean)instantiate(fbc);
if(!includePrototypes && !fb.isSingleton()){
continue;
}
final Class fbt = fb.getObjectType();
if(type==null || ReflectionUtil.isAssignableFrom(type, fbt)){
final String beanname = (String)key;
listener.onFactoryBean(beanname, fb);
continue;
}
}
if(!includePrototypes && !c.isSingleton()){
continue;
}
if(type==null || for_factorybean){
//if searching FactoryBean.class or null, we return the FactoryBean itself.
listener.onFactoryBean((String)key, fbc);
continue;
}
}
else{
if(!includePrototypes && !c.isSingleton()){
continue;
}
if(type==null){
listener.onBean((String)key, c);
continue;
}
final Class ctype = c.getType();
if(ctype!=null&&ReflectionUtil.isAssignableFrom(type, ctype)){
listener.onBean((String)key, c);
continue;
}
}
}
}
public String[] getBeanNamesForType(Class type, boolean includePrototypes,
boolean includeFactoryBeans){
/*
final boolean for_factorybean = type!=null&&FactoryBean.class.equals(type);
final ArrayList buf = new ArrayList();
for(Iterator it=yan.keys().iterator();it.hasNext();){
final Object key = it.next();
if(!(key instanceof String)) continue;
final jfun.yan.Component c = yan.getComponent(key);
final Component fbc = SpringAdapter.getFactoryBeanComponent(c);
if(fbc!=null){
//I am Factory Bean
if(includeFactoryBeans){
//attempt to match the FactoryBean result.
final FactoryBean fb = (FactoryBean)instantiate(fbc);
if(!includePrototypes && !fb.isSingleton()){
continue;
}
final Class fbt = fb.getObjectType();
if(type==null || ReflectionUtil.isAssignableFrom(type, fbt)){
final String beanname = (String)key;
buf.add(beanname);
continue;
}
}
if(!includePrototypes && !c.isSingleton()){
continue;
}
if(type==null || for_factorybean){
//if searching FactoryBean.class or null, we return the FactoryBean itself.
buf.add(BeanFactory.FACTORY_BEAN_PREFIX+key);
continue;
}
}
else{
if(!includePrototypes && !c.isSingleton()){
continue;
}
if(type==null){
buf.add(key);
continue;
}
final Class ctype = c.getType();
if(ctype!=null&&ReflectionUtil.isAssignableFrom(type, ctype)){
buf.add(key);
continue;
}
}
}*/
final ArrayList buf = new ArrayList();
final BeanListener listener = new BeanListener(){
public void onBean(String key, Component c) {
buf.add(key);
}
public void onFactoryBean(String key, Component fbc) {
buf.add(prefix+key);
}
public void onFactoryBean(String key, FactoryBean fb) {
buf.add(key);
}
};
foreachBean(type, includePrototypes, includeFactoryBeans, listener);
final String[] ret = new String[buf.size()];
buf.toArray(ret);
return ret;
}
/*
* Does this container contains a component with the given name?
* @param beanName the name of the component. If it starts with
* a "&", as specified by Spring, this "&" is chopped off
* before searching.
*/
public boolean containsBeanDefinition(String beanName) {
return yan.containsKey(getRealBeanName(beanName));
}
private static String getRealBeanName(String name){
return BeanFactoryUtils.transformedBeanName(name);
}
private static boolean isFactoryBeanType(Class type){
return type!=null && FactoryBean.class.equals(type);
}
/*
private FactoryBean getFactoryBean(Component c){
return new Component2FactoryBean(c, yan);
}*/
private Object instantiate(Component c){
return yan.instantiateComponent(c);
}
/**
* Get the (String,Object) map of all components with the given type.
* @param type the expected type. If this type is FactoryBean,
* the component is adapted to FactoryBean without actually instantiating
* instance.
* @return the Map object containing component instances (if the type
* is not BeanFactory), otherwise BeanFactory objects.
*/
public Map getBeansOfType(Class type) throws BeansException {
return getBeansOfType(type, true, true);
}
/**
* Get the (String,Object) map of all components with the given type.
* @param type the expected type.
* @param includePrototypes Are the components who may not be singletons included?
* @param includeFactoryBeans Are FactoryBean objects included?
* @return the Map object containing component instances.
*/
public Map getBeansOfType(Class type, boolean includePrototypes,
boolean includeFactoryBeans) throws BeansException {
/*
final String prefix = BeanFactory.FACTORY_BEAN_PREFIX;
final Map result = new LinkedHashMap();
if(isFactoryBeanType(type)){
for(Iterator it=yan.keys().iterator();it.hasNext();){
final Object key = it.next();
if(!(key instanceof String))
continue;
final String beanname = (String)key;
final Component c = yan.getComponent(key);
if(!includePrototypes && !c.isSingleton())
continue;
final Component fbc = SpringAdapter.getFactoryBeanComponent(c);
if(fbc!=null){
result.put(beanname, yan.instantiateComponent(fbc));
}
}
}
else{
for(Iterator it=yan.keys().iterator();it.hasNext();){
final Object key = it.next();
if(!(key instanceof String))
continue;
final Component c = yan.getComponent(key);
if(!includePrototypes && !c.isSingleton())
continue;
final Class ctype = c.getType();
if(ctype!=null && ReflectionUtil.isAssignableFrom(type, ctype)){
result.put(key, instantiate(c));
}
else if(includeFactoryBeans){
final Component fbc = SpringAdapter.getFactoryBeanComponent(c);
if(fbc!=null){
final FactoryBean fb = (FactoryBean)yan.instantiateComponent(fbc);
if(ReflectionUtil.isAssignableFrom(type, fb.getObjectType())){
final String beanname = (String)key;
try{
result.put(beanname, fb.getObject());
}
catch(RuntimeException e){
throw e;
}
catch(Error e){
throw e;
}
catch(Exception e){
throw new BeanCreationException((String)key, "FactoryBean failed", e);
}
}
}
}
}
}
return result;*/
final Map result = new LinkedHashMap();
final BeanListener listener = new BeanListener(){
public void onBean(String key, Component c) {
result.put(key, instantiate(c));
}
public void onFactoryBean(String key, Component fbc) {
result.put(prefix+key, instantiate(fbc));
}
public void onFactoryBean(String key, FactoryBean fb) {
result.put(key, instantiate(key, fb));
}
};
return result;
}
private Component findComponent(String name){
final Component c = yan.getComponent(name);
if(c==null){
throw new NoSuchBeanDefinitionException(name, "bean "+name+" not found.");
}
return c;
}
private Component getComponent(String name){
return findComponent(BeanFactoryUtils.transformedBeanName(name));
}
/**
* Get a bean object.
* @param name the name of the component.
* If the name starts with a "&", as specified by Spring,
* a FactoryBean object is returned corresponding to the component,
* otherwise the component itself is instantiated.
*/
public Object getBean(final String name) throws BeansException {
/*
if(BeanFactoryUtils.isFactoryDereference(name)){
final String factoryname = BeanFactoryUtils.transformedBeanName(name);
return getFactoryBean(findComponent(factoryname));
}
else{
return instantiate(findComponent(name));
}*/
return getBean(name, null);
}
/**
* Get a bean object.
* @param beanname the name of the component.
* If the name starts with a "&", as specified by Spring,
* a FactoryBean object is returned corresponding to the component,
* otherwise the component itself is instantiated.
* @param requiredType the expecting type. If the name starts with
* a "&", as specified by Spring convention, the type has to be
* either null or FactoryBean or a super type of FactoryBean.
* And in that case, a FactoryBean object is returned.
* If the name is a regular bean name, the corresponding component
* is instantiated.
*/
public Object getBean(final String beanname, Class requiredType) throws BeansException {
if(BeanFactoryUtils.isFactoryDereference(beanname)){
//return FactoryBean
final String factoryname = BeanFactoryUtils.transformedBeanName(beanname);
final Component c = findComponent(factoryname);
final Component fbc = SpringAdapter.getFactoryBeanComponent(c);
if(fbc!=null){
throw new NoSuchBeanDefinitionException(beanname,
"FactoryBean "+factoryname+" not found.");
}
final Object result = instantiate(fbc);
checkBeanInstanceType(beanname, requiredType, result);
return result;
}
else{
final Component c = findComponent(beanname);
final Component fbc = SpringAdapter.getFactoryBeanComponent(c);
if(requiredType==null){
return instantiate(c);
}
if(fbc!=null){
final FactoryBean fb = (FactoryBean)instantiate(fbc);
final Class ctype = fb.getObjectType();
checkBeanType(beanname, requiredType, ctype);
return instantiate(beanname, fb);
}
else{
final Class ctype = c.getType();
if(c.isConcrete()){
checkBeanType(beanname, requiredType, ctype);
}
final Object result = instantiate(c);
checkBeanInstanceType(beanname, requiredType, result);
return result;
}
}
}
/**
* Same as {@link #containsBeanDefinition(String)} since
* this class does not care about hierarchy.
* In fact, Yan does not create Spring hierarchies.
*/
public boolean containsBean(String name) {
return this.containsBeanDefinition(name);
//yan.containsKey(BeanFactoryUtils.transformedBeanName(name));
}
/**
* Is the component identified by the name a singleton?
* FactoryBean is instantiated if the name doesn't start with '&'
* and the bean is a FactoryBean.
*/
public boolean isSingleton(final String name)
throws NoSuchBeanDefinitionException {
final Component c = getComponent(name);
if(!BeanFactoryUtils.isFactoryDereference(name)){
final Component fbc = SpringAdapter.getFactoryBeanComponent(c);
if(fbc!=null){
final FactoryBean fb = (FactoryBean)instantiate(fbc);
return fb.isSingleton();
}
}
return c.isSingleton();
}
/**
* Get the static type of a component identified by the name.
* @param name the bean name. FactoryBean is instantiated if
* the name doesn't start with '&' and the bean is a FactoryBean.
*/
public Class getType(final String name)
throws NoSuchBeanDefinitionException {
final Component c = getComponent(name);
if(!BeanFactoryUtils.isFactoryDereference(name)){
final Component fbc = SpringAdapter.getFactoryBeanComponent(c);
if(fbc!=null){
final FactoryBean fb = (FactoryBean)instantiate(fbc);
return fb.getObjectType();
}
}
return c.getType();
}
private static final String[] no_aliases = {};
/**
* Always returns empty.
*/
public String[] getAliases(String name)
throws NoSuchBeanDefinitionException {
getComponent(name);
return no_aliases;
}
public String getDisplayName() {
return name;
}
public ApplicationContext getParent() {
return null;
}
public long getStartupDate() {
return startup_timestamp;
}
public void publishEvent(ApplicationEvent event) {
final ApplicationEventMulticaster multicaster = getApplicationEventMulticaster();
multicaster.multicastEvent(event);
if(parent_publisher!=null)
parent_publisher.publishEvent(event);
}
public String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException {
return getMessageSource().getMessage(code, args, locale);
}
public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
return getMessageSource().getMessage(resolvable, locale);
}
public String getMessage(String code, Object[] args, String defaultMessage, Locale locale) {
return getMessageSource().getMessage(code, args, defaultMessage, locale);
}
public Resource[] getResources(String locationPattern) throws IOException {
return resource_loader.getResources(locationPattern);
}
public Resource getResource(String location) {
return resource_loader.getResource(location);
}
public BeanFactory getParentBeanFactory() {
return null;
}
private void checkBeanType(String beanname, Class required, Class actual){
if(actual!=null && required!=null &&
!ReflectionUtil.isAssignableFrom(required, actual)){
throw new BeanNotOfRequiredTypeException(beanname, required, actual);
}
}
private void checkBeanInstanceType(String beanname, Class required, Object instance){
if(required!=null &&
!ReflectionUtil.isInstance(required, instance)){
throw new BeanNotOfRequiredTypeException(beanname, required,
instance==null?null:instance.getClass());
}
}
private static Object instantiate(String beanname, FactoryBean fb){
try{
return fb.getObject();
}
catch(RuntimeException e){
throw e;
}
catch(Error e){
throw e;
}
catch(Exception e){
throw new BeanCreationException(beanname, "FactoryBean failed", e);
}
}
}