View Javadoc
1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.ksb.api.bus.support;
17  
18  import org.apache.commons.lang.ArrayUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
21  import org.kuali.rice.core.api.resourceloader.ResourceLoaderException;
22  import org.kuali.rice.ksb.api.KsbApiServiceLocator;
23  import org.springframework.beans.factory.FactoryBean;
24  import org.springframework.beans.factory.FactoryBeanNotInitializedException;
25  import org.springframework.beans.factory.InitializingBean;
26  
27  import javax.xml.namespace.QName;
28  import java.lang.reflect.InvocationHandler;
29  import java.lang.reflect.InvocationTargetException;
30  import java.lang.reflect.Method;
31  import java.lang.reflect.Proxy;
32  
33  /**
34   * 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
35   * proxy interfaces that are injected into this bean, or derived from the objectType which is injected into this bean.
36   * If neither of these are injected, then an exception will be through during bean initialization.
37   *
38   * The attempt to fetch the resource from the ServiceBus won't be attempted until a method on the resulting
39   * proxy is invoked.  If it fails to locate the resource, it will throw a ResourceLoaderException indicating the service
40   * could not be loaded.
41   *
42   * <p>This allows for referencing of a potentially remote service in the spring context during startup which won't get
43   * used until after startup.  If the remote service gets used *during* startup, then it must be available from the GRL
44   * during startup or else the ResourceLoaderException will be thrown.</p>
45   *
46   * @author Kuali Rice Team (rice.collab@kuali.org)
47   *
48   */
49  public class LazyServiceFactoryBean implements FactoryBean<Object>, InitializingBean {
50  
51      private String serviceNamespace;
52  	private String serviceName;
53      private String applicationId;
54      private Class<?> objectType;
55      private Class<?>[] proxyInterfaces;
56      private Object proxyObject;
57  
58  	public LazyServiceFactoryBean() {
59          this.objectType = Object.class;
60          this.proxyInterfaces = null;
61  	}
62  
63      public void afterPropertiesSet() throws Exception {
64          if (ArrayUtils.isEmpty(getProxyInterfaces())) {
65              setProxyInterfaces(detectProxyInterfaces());
66              if (ArrayUtils.isEmpty(getProxyInterfaces())) {
67                  throw new FactoryBeanNotInitializedException("Failed to initialize factory bean because " +
68                          "proxyInterfaces were not injected or could not be derived from object type.");
69              }
70          }
71          this.proxyObject = Proxy.newProxyInstance(getClass().getClassLoader(), getProxyInterfaces(),
72                  new LazyInvocationHandler());
73  	}
74  
75      protected Class<?>[] detectProxyInterfaces() {
76          Class<?> type = getObjectType();
77          if (type != null && type.isInterface()) {
78              return new Class<?>[] {type};
79          } else if (type != null) {
80              return type.getInterfaces();
81          } else {
82              return null;
83          }
84      }
85  
86      @Override
87      public Object getObject() throws Exception {
88          return this.proxyObject;
89      }
90  
91      @Override
92      public Class<?> getObjectType() {
93          return this.objectType;
94      }
95  
96      public void setObjectType(Class<?> objectType) {
97          this.objectType = objectType;
98      }
99  
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 }