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 }