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