All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kuali.student.common.spring.WebServiceAwareSpringBeanPostProcessor Maven / Gradle / Ivy

/*
 * Copyright 2012 The Kuali Foundation
 *
 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
 *
 * 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.kuali.student.common.spring;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Set;

import javax.annotation.Resource;
import javax.inject.Inject;
import javax.jws.WebService;
import javax.xml.namespace.QName;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.util.ReflectionUtils;

/**
 * 
 * A Spring Bean PostProcessor designed for automatic lookup of auto wired
 * dependencies through the Rice GlobalResourceLoader.
 * 
 * In Kuali Student when we create services they are registered onto the Kuali
 * Service Bus where clients like the KRAD UI needs to consume them from.
 * 
 * This is a helper for resolving the services through the KSB using the
 * namespace and local name defined on the ks-api service @WebService
 * annotation.
 * 
 * @author Kuali Student Team
 * 
 */
public class WebServiceAwareSpringBeanPostProcessor extends AutowiredAnnotationBeanPostProcessor implements PriorityOrdered, BeanFactoryAware {

	private static final Logger log = LoggerFactory
			.getLogger(WebServiceAwareSpringBeanPostProcessor.class);
	
	private BeanFactory beanFactory;

	/**
	 * 
	 */
	public WebServiceAwareSpringBeanPostProcessor() {
	}

	
	
	/* (non-Javadoc)
	 * @see org.springframework.core.Ordered#getOrder()
	 */
	@Override
	public int getOrder() {
		return Ordered.HIGHEST_PRECEDENCE;
	}



	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		
		// intentionally does nothing
		return bean;
	}

	
	
	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter#postProcessAfterInstantiation(java.lang.Object, java.lang.String)
	 */
	@Override
	public boolean postProcessAfterInstantiation(Object bean, String beanName)
			throws BeansException {
		
		Class beanClass = bean.getClass();
		
		try {
			super.processInjection(bean);
			
		} catch (BeansException e2) {
			// any resolved beans from the local applicationContext will have been injected into the bean by now
			// so fall through.
			// we will only update the @Autowired fields that are null and declare @WebService annotations.
		}

		Field[] fields = beanClass.getDeclaredFields();

		for (Field field : fields) {

			// check for the @Autowired, @Resource, or @Value annotation
			if (!fieldHasInjectionAnnotation(field))
				continue; // skip fields that don't have an injection annotation

			
			Object currentValue = null;
			try {

				ReflectionUtils.makeAccessible(field);

				currentValue = field.get(bean);

			} catch (IllegalArgumentException e1) {
				log.warn("get error", e1);
				continue;

			} catch (SecurityException e1) {
				log.warn("get error", e1);
				continue;
			} catch (IllegalAccessException e1) {
				log.warn("get error", e1);
				continue;
			}

			// Only resolve using KSB if the object value has not been set.
			if (currentValue != null)
				continue; // was handled already

			Class fieldType = field.getType();
			
			WebService webServiceAnnotation = (WebService) fieldType.getAnnotation(
					WebService.class);
			
			if (webServiceAnnotation != null) {

				// we can only auto resolve if the @WebService annotation is
				// present.

				
				String namespace = webServiceAnnotation.targetNamespace();
				String serviceName = webServiceAnnotation.serviceName();
				
				if (serviceName.isEmpty())
					serviceName = webServiceAnnotation.name();

				if (namespace != null) {
					
					// First check to see if the application context has a reference 
					// using the serivceName
					
					try {
					
						Object service = null;
                        try {
	                        service = beanFactory.getBean(serviceName, fieldType);
                        } catch (NoSuchBeanDefinitionException e) {
                        	service = null;
                        	// fall through
                        }
					
						if (service != null) {
							injectServiceReference(bean, beanName, field, service, " from ApplicationContext using BeanName: " + serviceName);
							continue; // skip to the next field
						}
						// else service == null
							// now try to resolve using the ksb
					

							final QName name = new QName(namespace, serviceName);

					
						
						try {
								SerializableProxyInvokationHandler invocationHandler = new SerializableProxyInvokationHandler();
								
								invocationHandler.setServiceName(name);
								
								service = Proxy
										.newProxyInstance(
												getClass().getClassLoader(),
												new Class[] { fieldType },
												invocationHandler);

								// now we have the service, try to inject it.
								injectServiceReference(bean, beanName, field, service, " from KSB using QName: " + name);
								
						} catch (NullPointerException e1) {
							log.warn("RiceResourceLoader is not configured/initialized properly.", e1);
							// skip to the next field.
							continue;
						}
						
						
						
					
						
					} catch (Exception e) {
						log.error(
								"failed to lookup resource in GlobalResourceLoader ("
										+ namespace + ", " + serviceName + ")",
								e);
						continue;
					}
				}

			}
		}
		
		// skip over any of the other post processors
		return false;
	}

	

	



	/*
	 * Inject the service reference into the bean.
	 * 
	 * Logic has been split out to let the proxy service be used
	 * or if there is a bean in the application context that has the local name use that.
	 * 
	 */
	private void injectServiceReference(Object bean, String beanName,  
            Field field,
            Object service, String message) throws IllegalArgumentException, IllegalAccessException {
		
		String fieldName = field.getName();

		String setterMethodName = "set" + StringUtils.capitalize(fieldName);
		
		Class beanClass = bean.getClass();
		
		Class fieldType = field.getType();
		
		try {

			/*
			 * We use the set method because even though we can reach in and set the bean
			 * if it is final we still get an exception.
			 * This way it will always work.
			 * And since the bean's are presently being wired using XML the setters will exist.
			 * 
			 */
			Method setterMethod = beanClass.getMethod(
					setterMethodName, field.getType());

			ReflectionUtils.invokeMethod(setterMethod, bean,
					service);

			log.warn("RESOLVED: beanName = "  + beanName + ", fieldName = " + field.getName() + ", fieldType = " + fieldType.getName() + message);
			
			
		} catch (IllegalArgumentException e) {
			log.warn("set error", e);
		} catch (SecurityException e) {
			log.warn("set error", e);
		} catch (NoSuchMethodException e) {
			
			// try the field
			ReflectionUtils.makeAccessible(field);
			
			field.set(bean, service);
			log.warn("RESOLVED: beanName = "  + beanName + ", fieldName = " + field.getName() + ", fieldType = " + fieldType.getName() + message);
		}
	    
    }



	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#setBeanFactory(org.springframework.beans.factory.BeanFactory)
	 */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
	    this.beanFactory = beanFactory;
		super.setBeanFactory(beanFactory);
    }



	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {

		
		return bean;
	}

	private boolean fieldHasInjectionAnnotation(Field field) {

		// @Autowired, @Resource, or @Value annotation
		if (field.getAnnotation(Autowired.class) != null) {
			log.debug("detected @Autowired annotation on field: "
					+ field.getName());
			return true;
		}
		if (field.getAnnotation(Value.class) != null) {
			log.debug("detected @Value annotation on field: " + field.getName());
			return true;
		}
		if (field.getAnnotation(Resource.class) != null) {
			log.debug("detected @Resource annotation on field: "
					+ field.getName());
			return true;
		}
		if (field.getAnnotation(Inject.class) != null) {
			log.debug("detected @Inject annotation on field: "
					+ field.getName());
			return true;
		}

		// not an injectable annotation.
		return false;

	}
	
	

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy