001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.ksb.api.bus.support; 017 018import org.apache.commons.lang.ArrayUtils; 019import org.apache.commons.lang.StringUtils; 020import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader; 021import org.kuali.rice.core.api.resourceloader.ResourceLoaderException; 022import org.kuali.rice.ksb.api.KsbApiServiceLocator; 023import org.springframework.beans.factory.FactoryBean; 024import org.springframework.beans.factory.FactoryBeanNotInitializedException; 025import org.springframework.beans.factory.InitializingBean; 026 027import javax.xml.namespace.QName; 028import java.lang.reflect.InvocationHandler; 029import java.lang.reflect.InvocationTargetException; 030import java.lang.reflect.Method; 031import java.lang.reflect.Proxy; 032 033/** 034 * Loads a lazy proxy to a service from the {@link org.kuali.rice.ksb.api.bus.ServiceBus}. This proxy is created based on either the 035 * proxy interfaces that are injected into this bean, or derived from the objectType which is injected into this bean. 036 * If neither of these are injected, then an exception will be through during bean initialization. 037 * 038 * The attempt to fetch the resource from the ServiceBus won't be attempted until a method on the resulting 039 * proxy is invoked. If it fails to locate the resource, it will throw a ResourceLoaderException indicating the service 040 * could not be loaded. 041 * 042 * <p>This allows for referencing of a potentially remote service in the spring context during startup which won't get 043 * used until after startup. If the remote service gets used *during* startup, then it must be available from the GRL 044 * during startup or else the ResourceLoaderException will be thrown.</p> 045 * 046 * @author Kuali Rice Team (rice.collab@kuali.org) 047 * 048 */ 049public class LazyServiceFactoryBean implements FactoryBean<Object>, InitializingBean { 050 051 private String serviceNamespace; 052 private String serviceName; 053 private String applicationId; 054 private Class<?> objectType; 055 private Class<?>[] proxyInterfaces; 056 private Object proxyObject; 057 058 public LazyServiceFactoryBean() { 059 this.objectType = Object.class; 060 this.proxyInterfaces = null; 061 } 062 063 public void afterPropertiesSet() throws Exception { 064 if (ArrayUtils.isEmpty(getProxyInterfaces())) { 065 setProxyInterfaces(detectProxyInterfaces()); 066 if (ArrayUtils.isEmpty(getProxyInterfaces())) { 067 throw new FactoryBeanNotInitializedException("Failed to initialize factory bean because " + 068 "proxyInterfaces were not injected or could not be derived from object type."); 069 } 070 } 071 this.proxyObject = Proxy.newProxyInstance(getClass().getClassLoader(), getProxyInterfaces(), 072 new LazyInvocationHandler()); 073 } 074 075 protected Class<?>[] detectProxyInterfaces() { 076 Class<?> type = getObjectType(); 077 if (type != null && type.isInterface()) { 078 return new Class<?>[] {type}; 079 } else if (type != null) { 080 return type.getInterfaces(); 081 } else { 082 return null; 083 } 084 } 085 086 @Override 087 public Object getObject() throws Exception { 088 return this.proxyObject; 089 } 090 091 @Override 092 public Class<?> getObjectType() { 093 return this.objectType; 094 } 095 096 public void setObjectType(Class<?> objectType) { 097 this.objectType = objectType; 098 } 099 100 @Override 101 public boolean isSingleton() { 102 return true; 103 } 104 105 public String getServiceNamespace() { 106 return serviceNamespace; 107 } 108 109 public void setServiceNamespace(String serviceNamespace) { 110 this.serviceNamespace = serviceNamespace; 111 } 112 113 public String getServiceName() { 114 return serviceName; 115 } 116 117 public void setServiceName(String serviceName) { 118 this.serviceName = serviceName; 119 } 120 121 public String getApplicationId() { 122 return applicationId; 123 } 124 125 public void setApplicationId(String applicationId) { 126 this.applicationId = applicationId; 127 } 128 129 public Class<?>[] getProxyInterfaces() { 130 return proxyInterfaces; 131 } 132 133 public void setProxyInterfaces(Class<?>[] proxyInterfaces) { 134 this.proxyInterfaces = proxyInterfaces; 135 } 136 137 private class LazyInvocationHandler implements InvocationHandler { 138 private volatile Object service = null; 139 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 140 try { 141 if (service == null) { 142 QName name = new QName(getServiceNamespace(), getServiceName()); 143 if (StringUtils.isNotBlank(getApplicationId())) { 144 service = KsbApiServiceLocator.getServiceBus().getService(name, getApplicationId()); 145 } else { 146 service = KsbApiServiceLocator.getServiceBus().getService(name); 147 } 148 if (service == null) { 149 throw new ResourceLoaderException("Failed to locate resource with name: " + name); 150 } 151 } 152 return method.invoke(service, args); 153 } catch (InvocationTargetException e) { 154 throw e.getTargetException(); 155 } 156 } 157 158 } 159 160}