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; 016 017 import java.io.Serializable; 018 import java.lang.reflect.Modifier; 019 020 import org.apache.hivemind.internal.Module; 021 import org.apache.hivemind.internal.ser.ServiceSerializationHelper; 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 029 /** 030 * Class used to assist extension points in creating proxies. 031 * 032 * @author Howard Lewis Ship 033 */ 034 public final class ProxyBuilder 035 { 036 private Class _serviceInterface; 037 038 private ClassFab _classFab; 039 040 private String _type; 041 042 /** 043 * Constructs a new builder. The type will be incorporated into value returned by the 044 * <code>toString()</code> method. 045 * The generated proxy has an constructor that expects the extension point id as single parameter. 046 * 047 * @param type 048 * used as part of the <code>toString()</code> method's return value 049 * @param module 050 * the module that constructs the proxy 051 * @param serviceInterface 052 * the interface of the proxied class. 053 * @param declaredInterface 054 * the interface of the proxied class or the class itself if the proxied class is actually a POJO. 055 * @param outerProxy 056 * if false, then the proxy can extend the configuration points service interface always. 057 * If true and the declared interface is actually a bean class (not 058 * an interface), then the proxy will be a subclass of that bean. 059 */ 060 public ProxyBuilder(String type, Module module, Class serviceInterface, Class declaredInterface, boolean outerProxy) 061 { 062 _type = type; 063 _serviceInterface = serviceInterface; 064 065 ClassFactory factory = (ClassFactory) module.getService( 066 "hivemind.ClassFactory", 067 ClassFactory.class); 068 069 boolean extendBeanClass = outerProxy && !declaredInterface.isInterface(); 070 Class baseClass = extendBeanClass ? declaredInterface : Object.class; 071 072 _classFab = factory.newClass(ClassFabUtils.generateClassName(_serviceInterface), baseClass); 073 074 _classFab.addField("_extensionPointId", String.class); 075 076 _classFab.addConstructor(new Class[] { String.class }, null, "{ _extensionPointId = $1; }"); 077 078 if (!extendBeanClass) 079 _classFab.addInterface(_serviceInterface); 080 081 // Not exactly certain this will work with non-interface beans that already 082 // are serializable! 083 084 if (outerProxy) 085 addSerializable(); 086 } 087 088 /** @since 1.1 */ 089 private void addSerializable() 090 { 091 _classFab.addInterface(Serializable.class); 092 093 BodyBuilder bb = new BodyBuilder(); 094 095 bb.add( 096 "return {0}.getServiceSerializationSupport().getServiceTokenForService(_extensionPointId);", 097 ServiceSerializationHelper.class.getName()); 098 099 MethodSignature sig = new MethodSignature(Object.class, "writeReplace", null, null); 100 101 _classFab.addMethod(Modifier.PRIVATE, sig, bb.toString()); 102 } 103 104 public ClassFab getClassFab() 105 { 106 return _classFab; 107 } 108 109 /** 110 * @see #addServiceMethods(String, boolean) 111 */ 112 public void addServiceMethods(String indirection) 113 { 114 addServiceMethods(indirection, true); 115 } 116 117 /** 118 * Creates the service methods for the class. 119 * 120 * @param indirection 121 * the name of a variable, or a method invocation snippet, used to redirect the 122 * invocation on the proxy to the actual service implementation. 123 * @param addToString if true, a implementation of the toString method is generated that 124 * returns some info about the proxy 125 */ 126 public void addServiceMethods(String indirection, boolean addToString) 127 { 128 BodyBuilder builder = new BodyBuilder(); 129 130 MethodIterator mi = new MethodIterator(_serviceInterface); 131 while (mi.hasNext()) 132 { 133 MethodSignature m = mi.next(); 134 if( !_classFab.containsMethod( m ) ) 135 { 136 builder.clear(); 137 builder.begin(); 138 builder.add("return ($r) "); 139 builder.add(indirection); 140 builder.add("."); 141 builder.add(m.getName()); 142 builder.addln("($$);"); 143 builder.end(); 144 _classFab.addMethod(Modifier.PUBLIC, m, builder.toString()); 145 } 146 } 147 148 if (!mi.getToString() && addToString) 149 ClassFabUtils.addToStringMethod(_classFab, "<" + _type + " for \" + _extensionPointId + \"" 150 + "(" + _serviceInterface.getName() + ")>"); 151 } 152 }