001 /** 002 Licensed to the Apache Software Foundation (ASF) under one or more 003 contributor license agreements. See the NOTICE file distributed with 004 this work for additional information regarding copyright ownership. 005 The ASF licenses this file to You under the Apache License, Version 2.0 006 (the "License"); you may not use this file except in compliance with 007 the License. You may obtain a copy of the License at 008 009 http://www.apache.org/licenses/LICENSE-2.0 010 011 Unless required by applicable law or agreed to in writing, software 012 distributed under the License is distributed on an "AS IS" BASIS, 013 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 See the License for the specific language governing permissions and 015 limitations under the License. 016 */ 017 package org.apache.commons.inject.impl.bind; 018 019 import java.lang.annotation.Annotation; 020 import java.lang.reflect.Constructor; 021 import java.lang.reflect.Method; 022 import java.lang.reflect.Modifier; 023 import java.util.List; 024 025 import org.apache.commons.inject.api.IBinding; 026 import org.apache.commons.inject.api.IInjector; 027 import org.apache.commons.inject.api.IKey; 028 import org.apache.commons.inject.api.IPoint; 029 import org.apache.commons.inject.api.IProvider; 030 import org.apache.commons.inject.api.Key; 031 import org.apache.commons.inject.api.bind.IAnnotatedBindingBuilder; 032 import org.apache.commons.inject.api.bind.ILinkedBindingBuilder; 033 import org.apache.commons.inject.api.bind.IScopedBindingBuilder; 034 import org.apache.commons.inject.api.bind.Scopes; 035 import org.apache.commons.inject.api.bind.IBinder.IInjectionListener; 036 import org.apache.commons.inject.api.bind.IBinder.IInjectionParticipator; 037 import org.apache.commons.inject.impl.AbstractBaseProvider; 038 import org.apache.commons.inject.impl.AbstractScopedProvider; 039 import org.apache.commons.inject.impl.Introspector; 040 import org.apache.commons.inject.impl.ListPoint; 041 import org.apache.commons.inject.impl.MutableBindingSet; 042 import org.apache.commons.inject.impl.AbstractBindingSet.MappedKey; 043 044 import com.google.inject.Provider; 045 046 047 /** 048 * Default implementation of a binding builder. Implements 049 * {@link IAnnotatedBindingBuilder}, thus {@link IScopedBindingBuilder}, 050 * and {@link ILinkedBindingBuilder} as well. In other words: Under the 051 * hood, you are always using this one and only binding builder. 052 */ 053 public class DefaultBindingBuilder<T> implements IAnnotatedBindingBuilder<T> { 054 private final Class<T> sourceType; 055 private final IKey<T> sourceKey; 056 private Annotation sourceAnnotation; 057 private Class<? extends Annotation> sourceAnnotationType; 058 private T targetInstance; 059 private Class<? extends T> targetType; 060 private Constructor<? extends T> targetConstructor; 061 private Method targetMethod; 062 private Provider<? extends T> targetProvider; 063 private IProvider<? extends T> targetIProvider; 064 private Scopes scope; 065 066 public DefaultBindingBuilder(Class<T> pType) { 067 this(pType, Key.NO_NAME); 068 } 069 070 public DefaultBindingBuilder(Class<T> pType, String pName) { 071 sourceKey = new Key<T>(pType, pName); 072 sourceType = pType; 073 } 074 075 public DefaultBindingBuilder(IKey<T> pKey) { 076 sourceKey = pKey; 077 sourceType = null; 078 } 079 080 @Override 081 public void toInstance(T pInstance) { 082 if (pInstance == null) { 083 throw new NullPointerException("The target instance must not be null."); 084 } 085 checkNoTarget(); 086 targetInstance = pInstance; 087 asEagerSingleton(); 088 } 089 090 private void checkNoTarget() { 091 if (targetInstance != null 092 || targetType != null 093 || targetConstructor != null 094 || targetMethod != null 095 || targetProvider != null 096 || targetIProvider != null) { 097 throw new IllegalStateException("The methods " + TARGET_METHOD_LIST 098 + " are mutually exclusive, and may be invoked only once."); 099 } 100 } 101 102 private static final String SCOPE_METHOD_LIST 103 = "toInstance(Object), scope(Scopes), asEagerSingleton(), and asLazySingleton()"; 104 private static final String TARGET_METHOD_LIST 105 = "toInstance(Object), to(Class), to(Constructor), to(Method)," 106 + " to(Provider, Class), to(IProvider)"; 107 108 @Override 109 public IScopedBindingBuilder<T> to(Class<? extends T> pImplClass) { 110 if (pImplClass == null) { 111 throw new NullPointerException("The target class must not be null."); 112 } 113 checkNoTarget(); 114 targetType = pImplClass; 115 return this; 116 } 117 118 @Override 119 public IScopedBindingBuilder<T> to(Constructor<? extends T> pConstructor) { 120 if (pConstructor == null) { 121 throw new NullPointerException("The target constructor must not be null."); 122 } 123 checkNoTarget(); 124 targetConstructor = pConstructor; 125 return this; 126 } 127 128 @Override 129 public IScopedBindingBuilder<T> to(Method pFactoryMethod) { 130 if (pFactoryMethod == null) { 131 throw new NullPointerException("The target constructor must not be null."); 132 } 133 if (!Modifier.isStatic(pFactoryMethod.getModifiers())) { 134 throw new IllegalStateException("The target method must be static."); 135 } 136 if (pFactoryMethod.getReturnType().isPrimitive()) { 137 throw new IllegalStateException("The target method must return a non-primitive result."); 138 } 139 if (pFactoryMethod.getReturnType().isArray()) { 140 throw new IllegalStateException("The target method must return a single object, and not an array."); 141 } 142 if (Void.TYPE == pFactoryMethod.getReturnType()) { 143 throw new IllegalStateException("The target method must return a non-void result."); 144 } 145 checkNoTarget(); 146 targetMethod = pFactoryMethod; 147 return this; 148 } 149 150 @Override 151 public <S extends T> IScopedBindingBuilder<T> to(Class<S> pType, 152 Provider<S> pProvider) { 153 if (pType == null) { 154 throw new NullPointerException("The target type must not be null."); 155 } 156 if (pProvider == null) { 157 throw new NullPointerException("The target provider must not be null."); 158 } 159 checkNoTarget(); 160 targetType = pType; 161 targetProvider = pProvider; 162 return this; 163 } 164 165 @Override 166 public IScopedBindingBuilder<T> to(IProvider<? extends T> pProvider) { 167 if (pProvider == null) { 168 throw new NullPointerException("The target provider must not be null."); 169 } 170 checkNoTarget(); 171 targetIProvider = pProvider; 172 return this; 173 } 174 175 @Override 176 public void scope(Scopes pScope) { 177 if (pScope == null) { 178 throw new NullPointerException("The target scope must not be null."); 179 } 180 if (scope != null) { 181 throw new IllegalStateException("The methods " + SCOPE_METHOD_LIST 182 + " are mutually exclusive, and may be invoked only once."); 183 } 184 scope = pScope; 185 } 186 187 @Override 188 public void asEagerSingleton() { 189 scope(Scopes.EAGER_SINGLETON); 190 } 191 192 @Override 193 public void asLazySingleton() { 194 scope(Scopes.LAZY_SINGLETON); 195 } 196 197 @Override 198 public ILinkedBindingBuilder<T> annotatedWith(Annotation pAnnotation) { 199 if (pAnnotation == null) { 200 throw new NullPointerException("The annotation must not be null."); 201 } 202 if (sourceAnnotation != null) { 203 throw new IllegalStateException("The method annotatedWith(Annotation) must not be invoked twice."); 204 } 205 sourceAnnotation = pAnnotation; 206 return this; 207 } 208 209 @Override 210 public ILinkedBindingBuilder<T> annotatedWith( 211 Class<? extends Annotation> pAnnotationType) { 212 if (pAnnotationType == null) { 213 throw new NullPointerException("The annotation type must not be null."); 214 } 215 if (sourceAnnotationType != null) { 216 throw new IllegalStateException("The method annotatedWith(Class) must not be invoked twice."); 217 } 218 sourceAnnotationType = pAnnotationType; 219 return this; 220 } 221 222 public void build(MutableBindingSet pBindings, final List<IInjectionListener> pListeners, 223 final List<IInjectionParticipator> pParticipators) { 224 final Class<T> baseType = getBaseType(); 225 ListPoint<T> point = Introspector.getInstance().getPoint(baseType, pBindings); 226 final IKey<T> key = sourceKey; 227 if (pParticipators != null) { 228 for (IInjectionParticipator participator : pParticipators) { 229 final List<IPoint<Object>> points = participator.getPoints(key, baseType); 230 if (points != null) { 231 for (IPoint<Object> p : points) { 232 @SuppressWarnings("unchecked") 233 final IPoint<T> pnt = (IPoint<T>) p; 234 point.add(pnt); 235 } 236 } 237 } 238 } 239 if (pListeners != null && !pListeners.isEmpty()) { 240 point.add(new IPoint<T>(){ 241 @Override 242 public void injectTo(T pInstance, IInjector pInjector) { 243 for (IInjectionListener listener : pListeners) { 244 listener.initialized(key, pInstance); 245 } 246 } 247 }); 248 } 249 final IProvider<T> baseProvider = getBaseProvider(baseType, point, pBindings); 250 final IProvider<T> scopedProvider = getScopedProvider(baseProvider); 251 final IBinding<T> binding = new DefaultBinding<T>(scopedProvider, point); 252 final Annotation[] annotations; 253 if (sourceAnnotation == null) { 254 annotations = Key.NO_ANNOTATIONS; 255 } else { 256 annotations = new Annotation[]{ sourceAnnotation }; 257 } 258 final MappedKey<T> mkey = new MappedKey<T>(sourceKey.getType(), sourceKey.getName(), 259 annotations, sourceAnnotationType); 260 261 pBindings.add(mkey, binding); 262 } 263 264 private Class<T> getBaseType() { 265 if (targetInstance != null) { 266 @SuppressWarnings("unchecked") 267 final Class<T> cl = (Class<T>) targetInstance.getClass(); 268 return cl; 269 } 270 if (targetProvider != null) { 271 @SuppressWarnings("unchecked") 272 final Class<T> cl = (Class<T>) targetType; 273 return cl; 274 } 275 if (targetIProvider != null) { 276 @SuppressWarnings("unchecked") 277 final Class<T> cl = (Class<T>) targetIProvider.getType(); 278 return cl; 279 } 280 if (targetType != null) { 281 @SuppressWarnings("unchecked") 282 final Class<T> cl = (Class<T>) targetType; 283 return cl; 284 } 285 if (targetConstructor != null) { 286 @SuppressWarnings("unchecked") 287 final Class<T> cl = (Class<T>) targetConstructor.getDeclaringClass(); 288 return cl; 289 } 290 if (targetMethod != null) { 291 @SuppressWarnings("unchecked") 292 final Class<T> cl = (Class<T>) targetMethod.getReturnType(); 293 return cl; 294 } 295 if (sourceType == null) { 296 throw new IllegalStateException("Neither of the methods " 297 + TARGET_METHOD_LIST + " has been invoked on this binding builder," 298 + " which is required when binding a key."); 299 } 300 if (sourceType.isInterface() || Modifier.isAbstract(sourceType.getModifiers())) { 301 throw new IllegalStateException("Neither of the methods " 302 + TARGET_METHOD_LIST + " has been invoked on this binding builder, " 303 + " but cannot bind " + sourceType.getName() 304 + " as target type, because it is an interface, or an abstract class."); 305 } 306 return sourceType; 307 } 308 private AbstractBaseProvider<T> getBaseProvider(Class<T> pType, IPoint<T> pPoint, MutableBindingSet pBindings) { 309 if (targetInstance != null) { 310 return new AbstractBaseProvider<T>(pType, pPoint){ 311 @Override 312 public T get() { 313 return targetInstance; 314 } 315 }; 316 } 317 if (targetProvider != null) { 318 return new AbstractBaseProvider<T>(pType, pPoint){ 319 @Override 320 public T get() { 321 return (T) targetProvider.get(); 322 } 323 }; 324 } 325 if (targetIProvider != null) { 326 return new AbstractBaseProvider<T>(pType, pPoint){ 327 @Override 328 public T get() { 329 return (T) targetIProvider.get(); 330 } 331 332 }; 333 } 334 if (targetType != null) { 335 @SuppressWarnings("unchecked") 336 final Class<T> cl = (Class<T>) targetType; 337 final AbstractBaseProvider<T> abp = (AbstractBaseProvider<T>) Introspector.getInstance().getProvider(cl, pPoint, pBindings); 338 return abp; 339 } 340 if (targetConstructor != null) { 341 final AbstractBaseProvider<T> abp = (AbstractBaseProvider<T>) Introspector.getInstance().getProvider(targetConstructor, pBindings); 342 return abp; 343 } 344 if (targetMethod != null) { 345 @SuppressWarnings("unchecked") 346 final AbstractBaseProvider<T> abp = (AbstractBaseProvider<T>) Introspector.getInstance().getProvider(targetMethod, pBindings); 347 return abp; 348 } 349 if (sourceType != null) { 350 final AbstractBaseProvider<T> abp = (AbstractBaseProvider<T>) Introspector.getInstance().getProvider(sourceType, pPoint, pBindings); 351 return abp; 352 } 353 throw new IllegalStateException("Neither of the methods " 354 + TARGET_METHOD_LIST + " has been invoked on this binding builder."); 355 } 356 357 public AbstractScopedProvider<T> getScopedProvider(IProvider<T> pBaseProvider) { 358 if (scope == null) { 359 throw new IllegalStateException("Neither of the methods " 360 + SCOPE_METHOD_LIST + " has been invoked on this binding builder."); 361 362 } 363 switch(scope) { 364 case PER_CALL: 365 return new PerCallProvider<T>(pBaseProvider); 366 case EAGER_SINGLETON: 367 return new EagerSingletonProvider<T>(pBaseProvider); 368 case LAZY_SINGLETON: 369 return new LazySingletonProvider<T>(pBaseProvider); 370 default: 371 throw new IllegalStateException("Invalid scope: " + scope); 372 } 373 } 374 }