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.lib.impl;
016
017 import java.lang.reflect.Modifier;
018
019 import org.apache.hivemind.ApplicationRuntimeException;
020 import org.apache.hivemind.ServiceImplementationFactory;
021 import org.apache.hivemind.ServiceImplementationFactoryParameters;
022 import org.apache.hivemind.service.BodyBuilder;
023 import org.apache.hivemind.service.ClassFab;
024 import org.apache.hivemind.service.ClassFabUtils;
025 import org.apache.hivemind.service.ClassFactory;
026 import org.apache.hivemind.service.MethodIterator;
027 import org.apache.hivemind.service.MethodSignature;
028 import org.apache.hivemind.util.ConstructorUtils;
029 import org.apache.hivemind.util.PropertyAdaptor;
030 import org.apache.hivemind.util.PropertyUtils;
031
032 /**
033 * Factory that dynamically exposes a property of another service. A proxy is constructed that
034 * accesses the target service and obtains a property from that. The service interface of the
035 * constructed service must match the type of the exposed property.
036 *
037 * @author Howard Lewis Ship
038 */
039 public class ServicePropertyFactory implements ServiceImplementationFactory
040 {
041 private ClassFactory _classFactory;
042
043 public Object createCoreServiceImplementation(
044 ServiceImplementationFactoryParameters factoryParameters)
045 {
046 ServicePropertyFactoryParameter p = (ServicePropertyFactoryParameter) factoryParameters
047 .getParameters().get(0);
048
049 Object targetService = p.getService();
050 String propertyName = p.getPropertyName();
051
052 PropertyAdaptor pa = PropertyUtils.getPropertyAdaptor(targetService, propertyName);
053
054 String readMethodName = pa.getReadMethodName();
055
056 if (readMethodName == null)
057 throw new ApplicationRuntimeException(ImplMessages.servicePropertyNotReadable(
058 propertyName,
059 targetService), null, p.getLocation(), null);
060 Class serviceInterface = factoryParameters.getServiceInterface();
061
062 if (!(serviceInterface.isAssignableFrom(pa.getPropertyType())))
063 throw new ApplicationRuntimeException(ImplMessages.servicePropertyWrongType(
064 propertyName,
065 targetService,
066 pa.getPropertyType(),
067 serviceInterface), p.getLocation(), null);
068
069 // Now we're good to go.
070
071 String name = ClassFabUtils.generateClassName(serviceInterface);
072
073 ClassFab cf = _classFactory.newClass(name, Object.class);
074
075 addInfrastructure(cf, targetService, serviceInterface, propertyName, readMethodName);
076
077 addMethods(
078 cf,
079 factoryParameters.getServiceId(),
080 serviceInterface,
081 propertyName,
082 targetService);
083
084 Class proxyClass = cf.createClass();
085
086 try
087 {
088 return ConstructorUtils.invokeConstructor(proxyClass, new Object[]
089 { targetService });
090 }
091 catch (Throwable ex)
092 {
093 throw new ApplicationRuntimeException(ex.getMessage(), p.getLocation(), ex);
094 }
095 }
096
097 private void addInfrastructure(ClassFab cf, Object targetService, Class serviceInterface,
098 String propertyName, String readPropertyMethodName)
099 {
100 cf.addInterface(serviceInterface);
101
102 Class targetServiceClass = ClassFabUtils.getInstanceClass(targetService, serviceInterface);
103
104 cf.addField("_targetService", targetServiceClass);
105
106 cf.addConstructor(new Class[]
107 { targetServiceClass }, null, "{ super(); _targetService = $1; }");
108
109 BodyBuilder b = new BodyBuilder();
110
111 b.begin();
112 b.addln(
113 "{0} property = _targetService.{1}();",
114 serviceInterface.getName(),
115 readPropertyMethodName);
116
117 b.addln("if (property == null)");
118 b.add(" throw new java.lang.NullPointerException(");
119 b.addQuoted(ImplMessages.servicePropertyWasNull(propertyName, targetService));
120 b.addln(");");
121
122 b.addln("return property;");
123
124 b.end();
125
126 MethodSignature sig = new MethodSignature(serviceInterface, "_targetServiceProperty", null,
127 null);
128 cf.addMethod(Modifier.FINAL | Modifier.PRIVATE, sig, b.toString());
129 }
130
131 private void addMethods(ClassFab cf, String serviceId, Class serviceInterface,
132 String propertyName, Object targetService)
133 {
134 MethodIterator mi = new MethodIterator(serviceInterface);
135
136 while (mi.hasNext())
137 {
138 MethodSignature sig = mi.next();
139
140 String body = "return ($r) _targetServiceProperty()." + sig.getName() + "($$);";
141
142 cf.addMethod(Modifier.PUBLIC, sig, body);
143 }
144
145 if (!mi.getToString())
146 ClassFabUtils.addToStringMethod(cf, ImplMessages.servicePropertyToString(
147 serviceId,
148 serviceInterface,
149 propertyName,
150 targetService));
151 }
152
153 public void setClassFactory(ClassFactory factory)
154 {
155 _classFactory = factory;
156 }
157 }