001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.reef.tang.implementation.java; 020 021import org.apache.reef.tang.*; 022import org.apache.reef.tang.annotations.Name; 023import org.apache.reef.tang.exceptions.*; 024import org.apache.reef.tang.implementation.*; 025import org.apache.reef.tang.types.*; 026import org.apache.reef.tang.util.MonotonicHashSet; 027import org.apache.reef.tang.util.MonotonicSet; 028import org.apache.reef.tang.util.ReflectionUtilities; 029import org.apache.reef.tang.util.TracingMonotonicTreeMap; 030 031import java.lang.reflect.InvocationTargetException; 032import java.util.*; 033 034public class InjectorImpl implements Injector { 035 static final InjectionPlan<?> BUILDING = new InjectionPlan<Object>(null) { 036 @Override 037 public int getNumAlternatives() { 038 throw new UnsupportedOperationException(); 039 } 040 041 @Override 042 public String toString() { 043 return "BUILDING INJECTION PLAN"; 044 } 045 046 @Override 047 public boolean isAmbiguous() { 048 throw new UnsupportedOperationException(); 049 } 050 051 @Override 052 public boolean isInjectable() { 053 throw new UnsupportedOperationException(); 054 } 055 056 @Override 057 protected String toAmbiguousInjectString() { 058 throw new UnsupportedOperationException(); 059 } 060 061 @Override 062 protected String toInfeasibleInjectString() { 063 throw new UnsupportedOperationException(); 064 } 065 066 @Override 067 protected boolean isInfeasibleLeaf() { 068 throw new UnsupportedOperationException(); 069 } 070 071 @Override 072 public String toShallowString() { 073 throw new UnsupportedOperationException(); 074 } 075 076 }; 077 final Map<ClassNode<?>, Object> instances = new TracingMonotonicTreeMap<>(); 078 final Map<NamedParameterNode<?>, Object> namedParameterInstances = new TracingMonotonicTreeMap<>(); 079 private final Configuration c; 080 private final ClassHierarchy namespace; 081 private final JavaClassHierarchy javaNamespace; 082 private final Set<InjectionFuture<?>> pendingFutures = new HashSet<>(); 083 private boolean concurrentModificationGuard = false; 084 private Aspect aspect; 085 086 public InjectorImpl(Configuration c) throws BindException { 087 this.c = c; 088 this.namespace = c.getClassHierarchy(); 089 this.javaNamespace = (ClassHierarchyImpl) this.namespace; 090 } 091 092 private static InjectorImpl copy(InjectorImpl old, 093 Configuration... configurations) throws BindException { 094 final InjectorImpl i; 095 try { 096 final ConfigurationBuilder cb = old.c.newBuilder(); 097 for (Configuration c : configurations) { 098 cb.addConfiguration(c); 099 } 100 i = new InjectorImpl(cb.build()); 101 } catch (BindException e) { 102 throw new IllegalStateException( 103 "Unexpected error copying configuration!", e); 104 } 105 for (ClassNode<?> cn : old.instances.keySet()) { 106 if (cn.getFullName().equals(ReflectionUtilities.getFullName(Injector.class)) 107 || cn.getFullName().equals(ReflectionUtilities.getFullName(InjectorImpl.class))) { 108 // This would imply that we're treating injector as a singleton somewhere. It should be copied fresh each time. 109 throw new IllegalStateException(); 110 } 111 try { 112 ClassNode<?> new_cn = (ClassNode<?>) i.namespace.getNode(cn 113 .getFullName()); 114 i.instances.put(new_cn, old.instances.get(cn)); 115 } catch (BindException e) { 116 throw new IllegalStateException("Could not resolve name " 117 + cn.getFullName() + " when copying injector"); 118 } 119 } 120 // Copy references to the remaining (which must have been set with 121 // bindVolatileParameter()) 122 for (NamedParameterNode<?> np : old.namedParameterInstances.keySet()) { 123 // if (!builder.namedParameters.containsKey(np)) { 124 Object o = old.namedParameterInstances.get(np); 125 NamedParameterNode<?> new_np = (NamedParameterNode<?>) i.namespace 126 .getNode(np.getFullName()); 127 i.namedParameterInstances.put(new_np, o); 128 } 129 // Fork the aspect (if any) 130 if (old.aspect != null) { 131 i.bindAspect(old.aspect.createChildAspect()); 132 } 133 return i; 134 } 135 136 private void assertNotConcurrent() { 137 if (concurrentModificationGuard) { 138 throw new ConcurrentModificationException("Detected attempt to use Injector from within an injected constructor!"); 139 } 140 } 141 142 @SuppressWarnings("unchecked") 143 private <T> T getCachedInstance(ClassNode<T> cn) { 144 if (cn.getFullName().equals("org.apache.reef.tang.Injector")) { 145 return (T) this;// TODO: We should be insisting on injection futures here! .forkInjector(); 146 } else { 147 T t = (T) instances.get(cn); 148 if (t instanceof InjectionFuture) { 149 throw new IllegalStateException("Found an injection future in getCachedInstance: " + cn); 150 } 151 return t; 152 } 153 } 154 155 /** 156 * Produce a list of "interesting" constructors from a set of ClassNodes. 157 * <p/> 158 * Tang Constructors expose a isMoreSpecificThan function that embeds all 159 * a skyline query over the lattices. Precisely: 160 * <p/> 161 * Let candidateConstructors be the union of all constructors defined by 162 * ClassNodes in candidateImplementations. 163 * <p/> 164 * This function returns a set called filteredImplementations, defined as 165 * follows: 166 * <p/> 167 * For each member f of filteredConstructors, there does not exist 168 * a g in candidateConstructors s.t. g.isMoreSpecificThan(f). 169 */ 170 private <T> List<InjectionPlan<T>> filterCandidateConstructors( 171 final List<ClassNode<T>> candidateImplementations, 172 final Map<Node, InjectionPlan<?>> memo) { 173 174 final List<InjectionPlan<T>> sub_ips = new ArrayList<>(); 175 for (final ClassNode<T> thisCN : candidateImplementations) { 176 final List<Constructor<T>> constructors = new ArrayList<>(); 177 final List<ConstructorDef<T>> constructorList = new ArrayList<>(); 178 if (null != c.getLegacyConstructor(thisCN)) { 179 constructorList.add(c.getLegacyConstructor(thisCN)); 180 } 181 constructorList 182 .addAll(Arrays.asList(thisCN.getInjectableConstructors())); 183 184 for (ConstructorDef<T> def : constructorList) { 185 final List<InjectionPlan<?>> args = new ArrayList<InjectionPlan<?>>(); 186 final ConstructorArg[] defArgs = def.getArgs(); 187 188 for (final ConstructorArg arg : defArgs) { 189 if (!arg.isInjectionFuture()) { 190 try { 191 final Node argNode = namespace.getNode(arg.getName()); 192 buildInjectionPlan(argNode, memo); 193 args.add(memo.get(argNode)); 194 } catch (NameResolutionException e) { 195 throw new IllegalStateException("Detected unresolvable " 196 + "constructor arg while building injection plan. " 197 + "This should have been caught earlier!", e); 198 } 199 } else { 200 try { 201 args.add(new InjectionFuturePlan<>(namespace.getNode(arg 202 .getName()))); 203 } catch (NameResolutionException e) { 204 throw new IllegalStateException("Detected unresolvable " 205 + "constructor arg while building injection plan. " 206 + "This should have been caught earlier!", e); 207 } 208 } 209 } 210 final Constructor<T> constructor = new Constructor<T>(thisCN, def, 211 args.toArray(new InjectionPlan[0])); 212 constructors.add(constructor); 213 } 214 // The constructors are embedded in a lattice defined by 215 // isMoreSpecificThan(). We want to see if, amongst the injectable 216 // plans, there is a unique dominant plan, and select it. 217 218 // First, compute the set of injectable plans. 219 final List<Integer> liveIndices = new ArrayList<>(); 220 for (int i = 0; i < constructors.size(); i++) { 221 if (constructors.get(i).getNumAlternatives() > 0) { 222 liveIndices.add(i); 223 } 224 } 225 // Now, do an all-by-all comparison, removing indices that are dominated 226 // by others. 227 for (int i = 0; i < liveIndices.size(); i++) { 228 for (int j = i + 1; j < liveIndices.size(); j++) { 229 final ConstructorDef<T> ci = constructors.get(liveIndices.get(i)) 230 .getConstructorDef(); 231 final ConstructorDef<T> cj = constructors.get(liveIndices.get(j)) 232 .getConstructorDef(); 233 234 if (ci.isMoreSpecificThan(cj)) { 235 liveIndices.remove(j); 236 j--; 237 } else if (cj.isMoreSpecificThan(ci)) { 238 liveIndices.remove(i); 239 // Done with this inner loop invocation. Check the new ci. 240 i--; 241 break; 242 } 243 } 244 } 245 if (constructors.size() > 0) { 246 sub_ips.add(wrapInjectionPlans(thisCN, constructors, false, 247 liveIndices.size() == 1 ? liveIndices.get(0) : -1)); 248 } 249 } 250 return sub_ips; 251 } 252 253 @SuppressWarnings("unchecked") 254 private <T> InjectionPlan<T> buildClassNodeInjectionPlan(ClassNode<T> cn, 255 T cachedInstance, 256 ClassNode<ExternalConstructor<T>> externalConstructor, 257 ClassNode<T> boundImpl, 258 ClassNode<T> defaultImpl, 259 Map<Node, InjectionPlan<?>> memo) { 260 261 if (cachedInstance != null) { 262 return new JavaInstance<T>(cn, cachedInstance); 263 } else if (externalConstructor != null) { 264 buildInjectionPlan(externalConstructor, memo); 265 return new Subplan<>(cn, 0, (InjectionPlan<T>) memo.get(externalConstructor)); 266 } else if (boundImpl != null && !cn.equals(boundImpl)) { 267 // We need to delegate to boundImpl, so recurse. 268 buildInjectionPlan(boundImpl, memo); 269 return new Subplan<>(cn, 0, (InjectionPlan<T>) memo.get(boundImpl)); 270 } else if (defaultImpl != null && !cn.equals(defaultImpl)) { 271 buildInjectionPlan(defaultImpl, memo); 272 return new Subplan<>(cn, 0, (InjectionPlan<T>) memo.get(defaultImpl)); 273 } else { 274 // if we're here and there isn't a bound impl or a default impl, 275 // then we're bound / defaulted to ourselves, so don't add 276 // other impls to the list of things to consider. 277 final List<ClassNode<T>> candidateImplementations = new ArrayList<>(); 278 candidateImplementations.add(cn); 279 final List<InjectionPlan<T>> sub_ips = filterCandidateConstructors(candidateImplementations, memo); 280 if (sub_ips.size() == 1) { 281 return wrapInjectionPlans(cn, sub_ips, false, -1); 282 } else { 283 return wrapInjectionPlans(cn, sub_ips, true, -1); 284 } 285 } 286 } 287 288 @SuppressWarnings("unchecked") 289 private <T> InjectionPlan<T> wrapInjectionPlans(ClassNode<T> infeasibleNode, 290 List<? extends InjectionPlan<T>> list, boolean forceAmbiguous, int selectedIndex) { 291 if (list.size() == 0) { 292 return new Subplan<>(infeasibleNode); 293 } else if ((!forceAmbiguous) && list.size() == 1) { 294 return list.get(0); 295 } else { 296 return new Subplan<>(infeasibleNode, selectedIndex, list.toArray(new InjectionPlan[0])); 297 } 298 } 299 300 /** 301 * Parse the bound value of np. When possible, this returns a cached instance. 302 * 303 * @return null if np has not been bound. 304 * @throws ParseException 305 */ 306 @SuppressWarnings("unchecked") 307 private <T> T parseBoundNamedParameter(NamedParameterNode<T> np) { 308 final T ret; 309 310 @SuppressWarnings("rawtypes") 311 final Set<Object> boundSet = c.getBoundSet((NamedParameterNode) np); 312 if (!boundSet.isEmpty()) { 313 Set<T> ret2 = new MonotonicSet<>(); 314 for (Object o : boundSet) { 315 if (o instanceof String) { 316 try { 317 ret2.add(javaNamespace.parse(np, (String) o)); 318 } catch (ParseException e) { 319 // Parsability is now pre-checked in bindSet, so it should not be reached! 320 throw new IllegalStateException("Could not parse " + o + " which was passed into " + np + " FIXME: Parsability is not currently checked by bindSetEntry(Node,String)"); 321 } 322 } else if (o instanceof Node) { 323 ret2.add((T) o); 324 } else { 325 throw new IllegalStateException("Unexpected object " + o + " in bound set. Should consist of nodes and strings"); 326 } 327 } 328 return (T) ret2; 329 } 330 final List<Object> boundList = c.getBoundList((NamedParameterNode) np); 331 if (boundList != null) { 332 List<T> ret2 = new ArrayList<>(); 333 for (Object o : boundList) { 334 if (o instanceof String) { 335 try { 336 ret2.add(javaNamespace.parse(np, (String) o)); 337 } catch (ParseException e) { 338 // Parsability is now pre-checked in bindList, so it should not be reached! 339 throw new IllegalStateException("Could not parse " + o + " which was passed into " + np + " FIXME: " + 340 "Parsability is not currently checked by bindList(Node,List)"); 341 } 342 } else if (o instanceof Node) { 343 ret2.add((T) o); 344 } else { 345 throw new IllegalStateException("Unexpected object " + o + " in bound list. Should consist of nodes and " + 346 "strings"); 347 } 348 } 349 return (T) ret2; 350 } else if (namedParameterInstances.containsKey(np)) { 351 ret = (T) namedParameterInstances.get(np); 352 } else { 353 final String value = c.getNamedParameter(np); 354 if (value == null) { 355 ret = null; 356 } else { 357 try { 358 ret = javaNamespace.parse(np, value); 359 namedParameterInstances.put(np, ret); 360 } catch (BindException e) { 361 throw new IllegalStateException( 362 "Could not parse pre-validated value", e); 363 } 364 } 365 } 366 return ret; 367 } 368 369 @SuppressWarnings("unchecked") 370 private <T> ClassNode<T> parseDefaultImplementation(ClassNode<T> cn) { 371 if (cn.getDefaultImplementation() != null) { 372 try { 373 return (ClassNode<T>) javaNamespace.getNode(cn.getDefaultImplementation()); 374 } catch (ClassCastException | NameResolutionException e) { 375 throw new IllegalStateException("After validation, " + cn + " had a bad default implementation named " + cn.getDefaultImplementation(), e); 376 } 377 } else { 378 return null; 379 } 380 } 381 382 @SuppressWarnings({"unchecked"}) 383 private <T> void buildInjectionPlan(final Node n, 384 Map<Node, InjectionPlan<?>> memo) { 385 if (memo.containsKey(n)) { 386 if (BUILDING == memo.get(n)) { 387 StringBuilder loopyList = new StringBuilder("["); 388 for (Node node : memo.keySet()) { 389 if (memo.get(node) == BUILDING) { 390 loopyList.append(" " + node.getFullName()); 391 } 392 } 393 loopyList.append(" ]"); 394 throw new ClassHierarchyException("Detected loopy constructor involving " 395 + loopyList.toString()); 396 } else { 397 return; 398 } 399 } 400 memo.put(n, BUILDING); 401 final InjectionPlan<T> ip; 402 if (n instanceof NamedParameterNode) { 403 final NamedParameterNode<T> np = (NamedParameterNode<T>) n; 404 405 final T boundInstance = parseBoundNamedParameter(np); 406 final T defaultInstance = javaNamespace.parseDefaultValue(np); 407 final T instance = boundInstance != null ? boundInstance : defaultInstance; 408 409 if (instance instanceof Node) { 410 buildInjectionPlan((Node) instance, memo); 411 ip = new Subplan<T>(n, 0, (InjectionPlan<T>) memo.get(instance)); 412 } else if (instance instanceof Set) { 413 Set<T> entries = (Set<T>) instance; 414 Set<InjectionPlan<T>> plans = new MonotonicHashSet<>(); 415 for (T entry : entries) { 416 if (entry instanceof ClassNode) { 417 buildInjectionPlan((ClassNode<?>) entry, memo); 418 plans.add((InjectionPlan<T>) memo.get(entry)); 419 } else { 420 plans.add(new JavaInstance<T>(n, entry)); 421 } 422 423 } 424 ip = new SetInjectionPlan<T>(n, plans); 425 } else if (instance instanceof List) { 426 List<T> entries = (List<T>) instance; 427 List<InjectionPlan<T>> plans = new ArrayList<>(); 428 for (T entry : entries) { 429 if (entry instanceof ClassNode) { 430 buildInjectionPlan((ClassNode<?>) entry, memo); 431 plans.add((InjectionPlan<T>) memo.get(entry)); 432 } else { 433 plans.add(new JavaInstance<T>(n, entry)); 434 } 435 } 436 ip = new ListInjectionPlan<T>(n, plans); 437 } else { 438 ip = new JavaInstance<T>(np, instance); 439 } 440 } else if (n instanceof ClassNode) { 441 final ClassNode<T> cn = (ClassNode<T>) n; 442 443 // Any (or all) of the next four values might be null; that's fine. 444 final T cached = getCachedInstance(cn); 445 final ClassNode<T> boundImpl = c.getBoundImplementation(cn); 446 final ClassNode<T> defaultImpl = parseDefaultImplementation(cn); 447 final ClassNode<ExternalConstructor<T>> ec = c.getBoundConstructor(cn); 448 449 ip = buildClassNodeInjectionPlan(cn, cached, ec, boundImpl, defaultImpl, memo); 450 } else if (n instanceof PackageNode) { 451 throw new IllegalArgumentException( 452 "Request to instantiate Java package as object"); 453 } else { 454 throw new IllegalStateException( 455 "Type hierarchy contained unknown node type!:" + n); 456 } 457 memo.put(n, ip); 458 } 459 460 /** 461 * Return an injection plan for the given class / parameter name. 462 * 463 * @param n The name of an injectable class or interface, or a NamedParameter. 464 * @return 465 * @throws NameResolutionException 466 */ 467 public InjectionPlan<?> getInjectionPlan(final Node n) { 468 Map<Node, InjectionPlan<?>> memo = new HashMap<>(); 469 buildInjectionPlan(n, memo); 470 return memo.get(n); 471 } 472 473 @Override 474 public InjectionPlan<?> getInjectionPlan(String name) throws NameResolutionException { 475 return getInjectionPlan(namespace.getNode(name)); 476 } 477 478 @SuppressWarnings("unchecked") 479 public <T> InjectionPlan<T> getInjectionPlan(Class<T> name) { 480 return (InjectionPlan<T>) getInjectionPlan(javaNamespace.getNode(name)); 481 } 482 483 @Override 484 public boolean isInjectable(String name) throws NameResolutionException { 485 return getInjectionPlan(namespace.getNode(name)).isInjectable(); 486 } 487 488 @Override 489 public boolean isInjectable(Class<?> clazz) { 490 try { 491 return isInjectable(ReflectionUtilities.getFullName(clazz)); 492 } catch (NameResolutionException e) { 493 throw new IllegalStateException("Could not round trip " + clazz + " through ClassHierarchy", e); 494 } 495 } 496 497 @Override 498 public boolean isParameterSet(String name) throws NameResolutionException { 499 InjectionPlan<?> p = getInjectionPlan(namespace.getNode(name)); 500 return p.isInjectable(); 501 } 502 503 @Override 504 public boolean isParameterSet(Class<? extends Name<?>> name) 505 throws BindException { 506 return isParameterSet(name.getName()); 507 } 508 509 private <U> U getInstance(Node n) throws InjectionException { 510 assertNotConcurrent(); 511 @SuppressWarnings("unchecked") 512 InjectionPlan<U> plan = (InjectionPlan<U>) getInjectionPlan(n); 513 U u = (U) injectFromPlan(plan); 514 515 while (!pendingFutures.isEmpty()) { 516 Iterator<InjectionFuture<?>> i = pendingFutures.iterator(); 517 InjectionFuture<?> f = i.next(); 518 pendingFutures.remove(f); 519 f.get(); 520 } 521 return u; 522 } 523 524 @Override 525 public <U> U getInstance(Class<U> clazz) throws InjectionException { 526 if (Name.class.isAssignableFrom(clazz)) { 527 throw new InjectionException("getInstance() called on Name " 528 + ReflectionUtilities.getFullName(clazz) 529 + " Did you mean to call getNamedInstance() instead?"); 530 } 531 return getInstance(javaNamespace.getNode(clazz)); 532 } 533 534 @SuppressWarnings("unchecked") 535 @Override 536 public <U> U getInstance(String clazz) throws InjectionException, NameResolutionException { 537 return (U) getInstance(namespace.getNode(clazz)); 538 } 539 540 @Override 541 @SuppressWarnings("unchecked") 542 public <T> T getNamedInstance(Class<? extends Name<T>> clazz) 543 throws InjectionException { 544 return (T) getInstance(javaNamespace.getNode(clazz)); 545 } 546 547 public <T> T getNamedParameter(Class<? extends Name<T>> clazz) 548 throws InjectionException { 549 return getNamedInstance(clazz); 550 } 551 552 private <T> java.lang.reflect.Constructor<T> getConstructor( 553 ConstructorDef<T> constructor) throws ClassNotFoundException, 554 NoSuchMethodException, SecurityException { 555 @SuppressWarnings("unchecked") 556 Class<T> clazz = (Class<T>) javaNamespace.classForName(constructor 557 .getClassName()); 558 ConstructorArg[] args = constructor.getArgs(); 559 Class<?> parameterTypes[] = new Class[args.length]; 560 for (int i = 0; i < args.length; i++) { 561 if (args[i].isInjectionFuture()) { 562 parameterTypes[i] = InjectionFuture.class; 563 } else { 564 parameterTypes[i] = javaNamespace.classForName(args[i].getType()); 565 } 566 } 567 java.lang.reflect.Constructor<T> cons = clazz 568 .getDeclaredConstructor(parameterTypes); 569 cons.setAccessible(true); 570 return cons; 571 } 572 573 /** 574 * This gets really nasty now that constructors can invoke operations on us. 575 * The upshot is that we should check to see if instances have been 576 * registered by callees after each recursive invocation of injectFromPlan or 577 * constructor invocations. The error handling currently bails if the thing we 578 * just instantiated should be discarded. 579 * <p/> 580 * This could happen if (for instance), a constructor did a 581 * bindVolatileInstance of its own class to an instance, or somehow triggered 582 * an injection of itself with a different plan (an injection of itself with 583 * the same plan would lead to an infinite recursion, so it's not really our 584 * problem). 585 * 586 * @param plan 587 * @return 588 * @throws InjectionException 589 */ 590 @SuppressWarnings("unchecked") 591 private <T> T injectFromPlan(InjectionPlan<T> plan) throws InjectionException { 592 593 if (!plan.isFeasible()) { 594 throw new InjectionException("Cannot inject " + plan.getNode().getFullName() + ": " 595 + plan.toCantInjectString()); 596 } 597 if (plan.isAmbiguous()) { 598 throw new InjectionException("Cannot inject " + plan.getNode().getFullName() + " " 599 + plan.toCantInjectString()); 600 } 601 if (plan instanceof InjectionFuturePlan) { 602 InjectionFuturePlan<T> fut = (InjectionFuturePlan<T>) plan; 603 final String key = fut.getNode().getFullName(); 604 try { 605 InjectionFuture<?> ret = new InjectionFuture<>(this, javaNamespace.classForName(fut.getNode().getFullName())); 606 pendingFutures.add(ret); 607 return (T) ret; 608 } catch (ClassNotFoundException e) { 609 throw new InjectionException("Could not get class for " + key); 610 } 611 } else if (plan.getNode() instanceof ClassNode && null != getCachedInstance((ClassNode<T>) plan.getNode())) { 612 return getCachedInstance((ClassNode<T>) plan.getNode()); 613 } else if (plan instanceof JavaInstance) { 614 // TODO: Must be named parameter node. Check. 615// throw new IllegalStateException("Instance from plan not in Injector's set of instances?!?"); 616 return ((JavaInstance<T>) plan).instance; 617 } else if (plan instanceof Constructor) { 618 final Constructor<T> constructor = (Constructor<T>) plan; 619 final Object[] args = new Object[constructor.getArgs().length]; 620 final InjectionPlan<?>[] argPlans = constructor.getArgs(); 621 622 for (int i = 0; i < argPlans.length; i++) { 623 args[i] = injectFromPlan(argPlans[i]); 624 } 625 try { 626 concurrentModificationGuard = true; 627 T ret; 628 try { 629 ConstructorDef<T> def = (ConstructorDef<T>) constructor.getConstructorDef(); 630 java.lang.reflect.Constructor<T> c = getConstructor(def); 631 632 if (aspect != null) { 633 ret = aspect.inject(def, c, args); 634 } else { 635 ret = c.newInstance(args); 636 } 637 } catch (IllegalArgumentException e) { 638 StringBuilder sb = new StringBuilder("Internal Tang error? Could not call constructor " + constructor.getConstructorDef() + " with arguments ["); 639 for (Object o : args) { 640 sb.append("\n\t" + o); 641 } 642 sb.append("]"); 643 throw new IllegalStateException(sb.toString(), e); 644 } 645 if (ret instanceof ExternalConstructor) { 646 ret = ((ExternalConstructor<T>) ret).newInstance(); 647 } 648 instances.put(constructor.getNode(), ret); 649 return ret; 650 } catch (ReflectiveOperationException e) { 651 throw new InjectionException("Could not invoke constructor: " + plan, e instanceof InvocationTargetException ? e.getCause() : e); 652 } finally { 653 concurrentModificationGuard = false; 654 } 655 } else if (plan instanceof Subplan) { 656 Subplan<T> ambiguous = (Subplan<T>) plan; 657 return injectFromPlan(ambiguous.getDelegatedPlan()); 658 } else if (plan instanceof SetInjectionPlan) { 659 SetInjectionPlan<T> setPlan = (SetInjectionPlan<T>) plan; 660 Set<T> ret = new MonotonicHashSet<>(); 661 for (InjectionPlan<T> subplan : setPlan.getEntryPlans()) { 662 ret.add(injectFromPlan(subplan)); 663 } 664 return (T) ret; 665 } else if (plan instanceof ListInjectionPlan) { 666 ListInjectionPlan<T> listPlan = (ListInjectionPlan<T>) plan; 667 List<T> ret = new ArrayList<>(); 668 for (InjectionPlan<T> subplan : listPlan.getEntryPlans()) { 669 ret.add(injectFromPlan(subplan)); 670 } 671 return (T) ret; 672 } else { 673 throw new IllegalStateException("Unknown plan type: " + plan); 674 } 675 } 676 677 @Override 678 public <T> void bindVolatileInstance(Class<T> c, T o) throws BindException { 679 bindVolatileInstanceNoCopy(c, o); 680 } 681 682 @Override 683 public <T> void bindVolatileParameter(Class<? extends Name<T>> c, T o) 684 throws BindException { 685 bindVolatileParameterNoCopy(c, o); 686 } 687 688 <T> void bindVolatileInstanceNoCopy(Class<T> c, T o) throws BindException { 689 assertNotConcurrent(); 690 Node n = javaNamespace.getNode(c); 691 if (n instanceof ClassNode) { 692 ClassNode<?> cn = (ClassNode<?>) n; 693 Object old = getCachedInstance(cn); 694 if (old != null) { 695 throw new BindException("Attempt to re-bind instance. Old value was " 696 + old + " new value is " + o); 697 } 698 instances.put(cn, o); 699 } else { 700 throw new IllegalArgumentException("Expected Class but got " + c 701 + " (probably a named parameter)."); 702 } 703 } 704 705 <T> void bindVolatileParameterNoCopy(Class<? extends Name<T>> c, T o) 706 throws BindException { 707 Node n = javaNamespace.getNode(c); 708 if (n instanceof NamedParameterNode) { 709 NamedParameterNode<?> np = (NamedParameterNode<?>) n; 710 Object old = this.c.getNamedParameter(np); 711 if (old != null) { 712 // XXX need to get the binding site here! 713 throw new BindException( 714 "Attempt to re-bind named parameter " + ReflectionUtilities.getFullName(c) + ". Old value was [" + old 715 + "] new value is [" + o + "]"); 716 } 717 try { 718 namedParameterInstances.put(np, o); 719 } catch (IllegalArgumentException e) { 720 throw new BindException( 721 "Attempt to re-bind named parameter " + ReflectionUtilities.getFullName(c) + ". Old value was [" + old 722 + "] new value is [" + o + "]"); 723 724 } 725 } else { 726 throw new IllegalArgumentException("Expected Name, got " + c 727 + " (probably a class)"); 728 } 729 } 730 731 @Override 732 public Injector forkInjector() { 733 try { 734 return forkInjector(new Configuration[0]); 735 } catch (BindException e) { 736 throw new IllegalStateException(e); 737 } 738 } 739 740 @Override 741 public Injector forkInjector(Configuration... configurations) 742 throws BindException { 743 InjectorImpl ret; 744 ret = copy(this, configurations); 745 return ret; 746 } 747 748 @Override 749 public <T> void bindAspect(Aspect a) throws BindException { 750 if (aspect != null) { 751 throw new BindException("Attempt to re-bind aspect! old=" + aspect + " new=" + a); 752 } 753 aspect = a; 754 } 755 756 @Override 757 public Aspect getAspect() { 758 return aspect; 759 } 760}