001 // Copyright 2004, 2005 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.hivemind.impl.servicemodel;
016
017 import java.lang.reflect.Constructor;
018 import java.lang.reflect.Modifier;
019
020 import org.apache.hivemind.ApplicationRuntimeException;
021 import org.apache.hivemind.events.RegistryShutdownListener;
022 import org.apache.hivemind.impl.ConstructableServicePoint;
023 import org.apache.hivemind.impl.ProxyBuilder;
024 import org.apache.hivemind.internal.ServicePoint;
025 import org.apache.hivemind.service.BodyBuilder;
026 import org.apache.hivemind.service.ClassFab;
027 import org.apache.hivemind.service.MethodSignature;
028
029 /**
030 * Subclass of {@link org.apache.hivemind.impl.servicemodel.AbstractServiceModelImpl} which supports
031 * creation of a singleton service proxy (deferring the actual construction of the service until
032 * absolutely necessary). This is used with the singleton service type, which is the default.
033 *
034 * @author Howard Lewis Ship
035 */
036 public final class SingletonServiceModel extends AbstractServiceModelImpl
037 {
038 /**
039 * Name of a method in the deferred proxy that is used to obtain the constructed service.
040 */
041 protected static final String SERVICE_ACCESSOR_METHOD_NAME = "_service";
042
043 private Object _serviceProxy;
044
045 private SingletonInnerProxy _innerProxy;
046
047 private Object _constructedService;
048
049 public SingletonServiceModel(ConstructableServicePoint servicePoint)
050 {
051 super(servicePoint);
052 }
053
054 public synchronized Object getService()
055 {
056 if (_serviceProxy == null)
057 _serviceProxy = createSingletonProxy();
058
059 return _serviceProxy;
060 }
061
062 /**
063 * This is invoked by the proxy to create the actual implementation.
064 */
065 public synchronized Object getActualServiceImplementation()
066 {
067 if (_constructedService == null)
068 {
069 _constructedService = constructServiceImplementation();
070 registerWithShutdownCoordinator(_constructedService);
071 }
072
073 // The inner proxy needs the service to implement the service interface.
074 // For bean services (not interface services) with no interceptors,
075 // the implementation may be the bean provided by the factory ... which
076 // does not implement the service interface (which was created at runtime).
077 // So we introduce a "bridge" between the two.
078
079 Class serviceInterface = getServicePoint().getServiceInterface();
080
081 if (!serviceInterface.isInstance(_constructedService))
082 _constructedService = constructBridgeProxy(_constructedService);
083
084 return _constructedService;
085 }
086
087 /**
088 * Creates a proxy class for the service and then construct the class itself.
089 */
090 private Object createSingletonProxy()
091 {
092 if (_log.isDebugEnabled())
093 _log.debug("Creating SingletonProxy for service "
094 + getServicePoint().getExtensionPointId());
095
096 try
097 {
098
099 // Create the outer proxy, the one visible to client code (including
100 // other services). It is dependent on an inner proxy.
101
102 Class proxyClass = createSingletonProxyClass();
103
104 // Create the inner proxy, whose job is to replace itself
105 // when the first service method is invoked.
106
107 Class innerProxyClass = createInnerProxyClass(proxyClass);
108
109 // Create the outer proxy.
110
111 Constructor co = proxyClass.getConstructor(new Class[] { String.class });
112
113 Object result = co.newInstance(new Object[] { getServicePoint().getExtensionPointId() });
114
115 // The inner proxy's construct invokes a method on the
116 // outer proxy to connect the two.
117
118 Constructor c = innerProxyClass.getConstructor(new Class[]
119 { String.class, proxyClass, getClass() });
120
121 _innerProxy = (SingletonInnerProxy) c.newInstance(new Object[]
122 { getServicePoint().getExtensionPointId(), result, this });
123
124 RegistryShutdownListener asListener = (RegistryShutdownListener) result;
125
126 getServicePoint().addRegistryShutdownListener(asListener);
127
128 return result;
129 }
130 catch (Exception ex)
131 {
132 throw new ApplicationRuntimeException(ex);
133 }
134
135 }
136
137 /**
138 * Creates a class that implements the service interface. Implements a private synchronized
139 * method, _service(), that constructs the service as needed, and has each service interface
140 * method re-invoke on _service(). Adds a toString() method if the service interface does not
141 * define toString().
142 */
143 private Class createSingletonProxyClass()
144 {
145 ConstructableServicePoint servicePoint = getServicePoint();
146
147 ProxyBuilder proxyBuilder = new ProxyBuilder("SingletonProxy", servicePoint.getModule(),
148 servicePoint.getServiceInterface(), servicePoint.getDeclaredInterface(), true);
149
150 ClassFab classFab = proxyBuilder.getClassFab();
151
152 Class serviceInterface = servicePoint.getServiceInterface();
153
154 // This will initally be the inner proxy, then switch over to the
155 // service implementation.
156
157 classFab.addField("_inner", serviceInterface);
158 classFab.addField("_shutdown", boolean.class);
159 if (!RegistryShutdownListener.class.isAssignableFrom(serviceInterface))
160 {
161 classFab.addInterface(RegistryShutdownListener.class);
162
163 classFab.addMethod(Modifier.PUBLIC | Modifier.FINAL, new MethodSignature(void.class,
164 "registryDidShutdown", null, null), "{ _shutdown = true; }");
165 }
166 classFab.addMethod(
167 Modifier.PUBLIC | Modifier.SYNCHRONIZED | Modifier.FINAL,
168 new MethodSignature(void.class, "_setInner", new Class[]
169 { serviceInterface }, null),
170 "{ _inner = $1; }");
171
172 BodyBuilder builder = new BodyBuilder();
173 builder.begin();
174 builder.addln("if (_shutdown)");
175 builder.begin();
176 builder.addln("_inner = null;");
177 builder.addln("throw org.apache.hivemind.HiveMind#createRegistryShutdownException();");
178 builder.end();
179
180 builder.addln("return _inner;");
181 builder.end();
182
183 classFab.addMethod(Modifier.PRIVATE, new MethodSignature(serviceInterface, "_getInner",
184 null, null), builder.toString());
185
186 proxyBuilder.addServiceMethods("_getInner()");
187
188 return classFab.createClass();
189 }
190
191 private Class createInnerProxyClass(Class deferredProxyClass)
192 {
193 ServicePoint servicePoint = getServicePoint();
194
195 Class serviceInterface = servicePoint.getServiceInterface();
196
197 ProxyBuilder builder = new ProxyBuilder("InnerProxy", servicePoint.getModule(),
198 serviceInterface, servicePoint.getDeclaredInterface(), false);
199
200 ClassFab classFab = builder.getClassFab();
201
202 classFab.addField("_deferredProxy", deferredProxyClass);
203 classFab.addField("_service", serviceInterface);
204 classFab.addField("_serviceModel", getClass());
205
206 BodyBuilder body = new BodyBuilder();
207
208 // The constructor remembers the outer proxy and registers itself
209 // with the outer proxy.
210
211 body.begin();
212
213 body.addln("this($1);");
214 body.addln("_deferredProxy = $2;");
215 body.addln("_serviceModel = $3;");
216 body.addln("_deferredProxy._setInner(this);");
217
218 body.end();
219
220 classFab.addConstructor(new Class[]
221 { String.class, deferredProxyClass, getClass() }, null, body.toString());
222
223 // Method _service() will look up the service implementation,
224 // then update the deferred proxy to go directly to the
225 // service implementation, bypassing itself!
226
227 body.clear();
228 body.begin();
229
230 body.add("if (_service == null)");
231 body.begin();
232
233 body.add("_service = (");
234 body.add(serviceInterface.getName());
235 body.addln(") _serviceModel.getActualServiceImplementation();");
236
237 body.add("_deferredProxy._setInner(_service);");
238
239 body.end();
240
241 body.add("return _service;");
242
243 body.end();
244
245 classFab.addMethod(
246 Modifier.PRIVATE | Modifier.FINAL | Modifier.SYNCHRONIZED,
247 new MethodSignature(serviceInterface, "_service", null, null),
248 body.toString());
249
250 builder.addServiceMethods("_service()");
251
252 // Build the implementation of interface SingletonInnerProxy
253
254 body.clear();
255 body.begin();
256
257 body.add("_service();");
258
259 body.end();
260
261 classFab.addMethod(Modifier.PUBLIC | Modifier.FINAL, new MethodSignature(void.class,
262 "_instantiateServiceImplementation", null, null), body.toString());
263
264 classFab.addInterface(SingletonInnerProxy.class);
265
266 return classFab.createClass();
267 }
268
269 public void instantiateService()
270 {
271 // Ensure that the outer and inner proxies have been created
272
273 getService();
274
275 // Force the inner proxy to resolve the service and install the result into
276 // the outer proxy.
277
278 _innerProxy._instantiateServiceImplementation();
279 }
280
281 }