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.strategy; 016 017 import java.lang.reflect.Constructor; 018 import java.lang.reflect.Modifier; 019 import java.util.Iterator; 020 import java.util.List; 021 022 import org.apache.hivemind.ApplicationRuntimeException; 023 import org.apache.hivemind.HiveMind; 024 import org.apache.hivemind.ServiceImplementationFactory; 025 import org.apache.hivemind.ServiceImplementationFactoryParameters; 026 import org.apache.hivemind.lib.util.StrategyRegistry; 027 import org.apache.hivemind.lib.util.StrategyRegistryImpl; 028 import org.apache.hivemind.service.ClassFab; 029 import org.apache.hivemind.service.ClassFabUtils; 030 import org.apache.hivemind.service.ClassFactory; 031 import org.apache.hivemind.service.MethodIterator; 032 import org.apache.hivemind.service.MethodSignature; 033 034 /** 035 * Implementation of the <code>hivemind.lib.StrategyFactory</code> service that constructs a 036 * service where the first parameter of each method is used to selecte a strategy from an 037 * {@link org.apache.hivemind.lib.util.StrategyRegistry}. The method invocation is then delegated 038 * to the strategy instance. 039 * <p> 040 * The service factory parameter defines a configuration (of 041 * {@link org.apache.hivemind.lib.strategy.StrategyContribution}s) that provide the mapping from 042 * Java classes (or interfaces) to adapter instances. 043 * 044 * @author Howard M. Lewis Ship 045 * @since 1.1 046 */ 047 public class StrategyFactory implements ServiceImplementationFactory 048 { 049 private ClassFactory _classFactory; 050 051 public Object createCoreServiceImplementation( 052 ServiceImplementationFactoryParameters factoryParameters) 053 { 054 StrategyRegistry ar = new StrategyRegistryImpl(); 055 056 buildRegistry(factoryParameters, ar); 057 058 Class implClass = buildImplementationClass(factoryParameters); 059 060 try 061 { 062 Constructor c = implClass.getConstructors()[0]; 063 064 return c.newInstance(new Object[] 065 { ar }); 066 } 067 catch (Exception ex) 068 { 069 throw new ApplicationRuntimeException(ex.getMessage(), HiveMind 070 .getLocation(factoryParameters.getFirstParameter()), ex); 071 } 072 073 } 074 075 // package private for testing purposes 076 077 void buildRegistry(ServiceImplementationFactoryParameters factoryParameters, StrategyRegistry ar) 078 { 079 Class serviceInterface = factoryParameters.getServiceInterface(); 080 081 StrategyParameter p = (StrategyParameter) factoryParameters.getFirstParameter(); 082 083 List contributions = p.getContributions(); 084 085 Iterator i = contributions.iterator(); 086 087 while (i.hasNext()) 088 { 089 StrategyContribution c = (StrategyContribution) i.next(); 090 091 try 092 { 093 Object adapter = c.getStrategy(); 094 095 if (!serviceInterface.isAssignableFrom(adapter.getClass())) 096 throw new ClassCastException(StrategyMessages.strategyWrongInterface(adapter, c 097 .getRegisterClass(), serviceInterface)); 098 099 ar.register(c.getRegisterClass(), adapter); 100 } 101 catch (Exception ex) 102 { 103 factoryParameters.getErrorLog().error(ex.getMessage(), c.getLocation(), ex); 104 } 105 106 } 107 108 } 109 110 // package private for testing purposes 111 112 private Class buildImplementationClass(ServiceImplementationFactoryParameters factoryParameters) 113 { 114 String name = ClassFabUtils.generateClassName(factoryParameters.getServiceInterface()); 115 116 return buildImplementationClass(factoryParameters, name); 117 } 118 119 // package private for testing purposes 120 121 Class buildImplementationClass(ServiceImplementationFactoryParameters factoryParameters, 122 String name) 123 { 124 Class serviceInterface = factoryParameters.getServiceInterface(); 125 126 ClassFab cf = _classFactory.newClass(name, Object.class); 127 128 cf.addInterface(serviceInterface); 129 130 cf.addField("_registry", StrategyRegistry.class); 131 132 cf.addConstructor(new Class[] 133 { StrategyRegistry.class }, null, "_registry = $1;"); 134 135 // TODO: Should we add a check for $1 == null? 136 137 cf.addMethod(Modifier.PRIVATE, new MethodSignature(serviceInterface, "_getStrategy", 138 new Class[] 139 { Object.class }, null), "return (" + serviceInterface.getName() 140 + ") _registry.getStrategy($1.getClass());"); 141 142 MethodIterator i = new MethodIterator(serviceInterface); 143 144 while (i.hasNext()) 145 { 146 MethodSignature sig = i.next(); 147 148 if (proper(sig)) 149 { 150 addAdaptedMethod(cf, sig); 151 } 152 else 153 { 154 ClassFabUtils.addNoOpMethod(cf, sig); 155 156 factoryParameters.getErrorLog().error( 157 StrategyMessages.improperServiceMethod(sig), 158 HiveMind.getLocation(factoryParameters.getFirstParameter()), 159 null); 160 } 161 162 } 163 164 if (!i.getToString()) 165 ClassFabUtils.addToStringMethod(cf, StrategyMessages.toString(factoryParameters 166 .getServiceId(), serviceInterface)); 167 168 return cf.createClass(); 169 } 170 171 private void addAdaptedMethod(ClassFab cf, MethodSignature sig) 172 { 173 String body = "return ($r) _getStrategy($1)." + sig.getName() + "($$);"; 174 175 cf.addMethod(Modifier.PUBLIC, sig, body); 176 } 177 178 /** 179 * A "proper" method is one with at least one parameter and whose first parameter is an object 180 * (not primitive) type. 181 */ 182 183 private boolean proper(MethodSignature sig) 184 { 185 Class[] parameterTypes = sig.getParameterTypes(); 186 187 return parameterTypes != null && parameterTypes.length > 0 188 && !parameterTypes[0].isPrimitive(); 189 } 190 191 public void setClassFactory(ClassFactory classFactory) 192 { 193 _classFactory = classFactory; 194 } 195 }