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 }