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 private final Map<ClassNode<?>, Object> instances = new TracingMonotonicTreeMap<>(); 078 private 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(final 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(final InjectorImpl old, 093 final Configuration... configurations) throws BindException { 094 final InjectorImpl i; 095 try { 096 final ConfigurationBuilder cb = old.c.newBuilder(); 097 for (final Configuration c : configurations) { 098 cb.addConfiguration(c); 099 } 100 i = new InjectorImpl(cb.build()); 101 } catch (final BindException e) { 102 throw new IllegalStateException( 103 "Unexpected error copying configuration!", e); 104 } 105 for (final 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 final ClassNode<?> newCn = (ClassNode<?>) i.namespace.getNode(cn 113 .getFullName()); 114 i.instances.put(newCn, old.instances.get(cn)); 115 } catch (final BindException e) { 116 throw new IllegalStateException("Could not resolve name " 117 + cn.getFullName() + " when copying injector", e); 118 } 119 } 120 // Copy references to the remaining (which must have been set with 121 // bindVolatileParameter()) 122 for (final NamedParameterNode<?> np : old.namedParameterInstances.keySet()) { 123 // if (!builder.namedParameters.containsKey(np)) { 124 final Object o = old.namedParameterInstances.get(np); 125 final NamedParameterNode<?> newNp = (NamedParameterNode<?>) i.namespace 126 .getNode(np.getFullName()); 127 i.namedParameterInstances.put(newNp, 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 " + 139 "from within an injected constructor!"); 140 } 141 } 142 143 @SuppressWarnings("unchecked") 144 private <T> T getCachedInstance(final ClassNode<T> cn) { 145 if (cn.getFullName().equals("org.apache.reef.tang.Injector")) { 146 return (T) this; // TODO: We should be insisting on injection futures here! .forkInjector(); 147 } else { 148 final T t = (T) instances.get(cn); 149 if (t instanceof InjectionFuture) { 150 throw new IllegalStateException("Found an injection future in getCachedInstance: " + cn); 151 } 152 return t; 153 } 154 } 155 156 /** 157 * Produce a list of "interesting" constructors from a set of ClassNodes. 158 * <p> 159 * Tang Constructors expose a isMoreSpecificThan function that embeds all 160 * a skyline query over the lattices. Precisely: 161 * <p> 162 * Let candidateConstructors be the union of all constructors defined by 163 * ClassNodes in candidateImplementations. 164 * <p> 165 * This function returns a set called filteredImplementations, defined as 166 * follows: 167 * <p> 168 * For each member f of filteredConstructors, there does not exist 169 * a g in candidateConstructors s.t. g.isMoreSpecificThan(f). 170 */ 171 private <T> List<InjectionPlan<T>> filterCandidateConstructors( 172 final List<ClassNode<T>> candidateImplementations, 173 final Map<Node, InjectionPlan<?>> memo) { 174 175 final List<InjectionPlan<T>> subIps = new ArrayList<>(); 176 for (final ClassNode<T> thisCN : candidateImplementations) { 177 final List<Constructor<T>> constructors = new ArrayList<>(); 178 final List<ConstructorDef<T>> constructorList = new ArrayList<>(); 179 if (null != c.getLegacyConstructor(thisCN)) { 180 constructorList.add(c.getLegacyConstructor(thisCN)); 181 } 182 constructorList 183 .addAll(Arrays.asList(thisCN.getInjectableConstructors())); 184 185 for (final ConstructorDef<T> def : constructorList) { 186 final List<InjectionPlan<?>> args = new ArrayList<>(); 187 final ConstructorArg[] defArgs = def.getArgs(); 188 189 for (final ConstructorArg arg : defArgs) { 190 if (!arg.isInjectionFuture()) { 191 try { 192 final Node argNode = namespace.getNode(arg.getName()); 193 buildInjectionPlan(argNode, memo); 194 args.add(memo.get(argNode)); 195 } catch (final NameResolutionException e) { 196 throw new IllegalStateException("Detected unresolvable " 197 + "constructor arg while building injection plan. " 198 + "This should have been caught earlier!", e); 199 } 200 } else { 201 try { 202 args.add(new InjectionFuturePlan<>(namespace.getNode(arg.getName()))); 203 } catch (final 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<>(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)).getConstructorDef(); 230 final ConstructorDef<T> cj = constructors.get(liveIndices.get(j)).getConstructorDef(); 231 232 if (ci.isMoreSpecificThan(cj)) { 233 liveIndices.remove(j); 234 j--; 235 } else if (cj.isMoreSpecificThan(ci)) { 236 liveIndices.remove(i); 237 // Done with this inner loop invocation. Check the new ci. 238 i--; 239 break; 240 } 241 } 242 } 243 if (constructors.size() > 0) { 244 subIps.add(wrapInjectionPlans(thisCN, constructors, false, 245 liveIndices.size() == 1 ? liveIndices.get(0) : -1)); 246 } 247 } 248 return subIps; 249 } 250 251 @SuppressWarnings("unchecked") 252 private <T> InjectionPlan<T> buildClassNodeInjectionPlan(final ClassNode<T> cn, 253 final T cachedInstance, 254 final ClassNode<ExternalConstructor<T>> externalConstructor, 255 final ClassNode<T> boundImpl, 256 final ClassNode<T> defaultImpl, 257 final Map<Node, InjectionPlan<?>> memo) { 258 259 if (cachedInstance != null) { 260 return new JavaInstance<T>(cn, cachedInstance); 261 } else if (externalConstructor != null) { 262 buildInjectionPlan(externalConstructor, memo); 263 return new Subplan<>(cn, 0, (InjectionPlan<T>) memo.get(externalConstructor)); 264 } else if (boundImpl != null && !cn.equals(boundImpl)) { 265 // We need to delegate to boundImpl, so recurse. 266 buildInjectionPlan(boundImpl, memo); 267 return new Subplan<>(cn, 0, (InjectionPlan<T>) memo.get(boundImpl)); 268 } else if (defaultImpl != null && !cn.equals(defaultImpl)) { 269 buildInjectionPlan(defaultImpl, memo); 270 return new Subplan<>(cn, 0, (InjectionPlan<T>) memo.get(defaultImpl)); 271 } else { 272 // if we're here and there isn't a bound impl or a default impl, 273 // then we're bound / defaulted to ourselves, so don't add 274 // other impls to the list of things to consider. 275 final List<ClassNode<T>> candidateImplementations = new ArrayList<>(); 276 candidateImplementations.add(cn); 277 final List<InjectionPlan<T>> subIps = filterCandidateConstructors(candidateImplementations, memo); 278 if (subIps.size() == 1) { 279 return wrapInjectionPlans(cn, subIps, false, -1); 280 } else { 281 return wrapInjectionPlans(cn, subIps, true, -1); 282 } 283 } 284 } 285 286 @SuppressWarnings("unchecked") 287 private <T> InjectionPlan<T> wrapInjectionPlans(final ClassNode<T> infeasibleNode, 288 final List<? extends InjectionPlan<T>> list, 289 final boolean forceAmbiguous, 290 final 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(final 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 final Set<T> ret2 = new MonotonicSet<>(); 314 for (final Object o : boundSet) { 315 if (o instanceof String) { 316 try { 317 ret2.add(javaNamespace.parse(np, (String) o)); 318 } catch (final 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 + 321 " FIXME: Parsability is not currently checked by bindSetEntry(Node,String)", e); 322 } 323 } else if (o instanceof Node) { 324 ret2.add((T) o); 325 } else { 326 throw new IllegalStateException("Unexpected object " + o + " in bound set. " + 327 "Should consist of nodes and strings"); 328 } 329 } 330 return (T) ret2; 331 } 332 final List<Object> boundList = c.getBoundList((NamedParameterNode) np); 333 if (boundList != null) { 334 final List<T> ret2 = new ArrayList<>(); 335 for (final Object o : boundList) { 336 if (o instanceof String) { 337 try { 338 ret2.add(javaNamespace.parse(np, (String) o)); 339 } catch (final ParseException e) { 340 // Parsability is now pre-checked in bindList, so it should not be reached! 341 throw new IllegalStateException("Could not parse " + o + " which was passed into " + np + " FIXME: " + 342 "Parsability is not currently checked by bindList(Node,List)", e); 343 } 344 } else if (o instanceof Node) { 345 ret2.add((T) o); 346 } else { 347 throw new IllegalStateException("Unexpected object " + o + " in bound list. Should consist of nodes and " + 348 "strings"); 349 } 350 } 351 return (T) ret2; 352 } else if (namedParameterInstances.containsKey(np)) { 353 ret = (T) namedParameterInstances.get(np); 354 } else { 355 final String value = c.getNamedParameter(np); 356 if (value == null) { 357 ret = null; 358 } else { 359 try { 360 ret = javaNamespace.parse(np, value); 361 namedParameterInstances.put(np, ret); 362 } catch (final BindException e) { 363 throw new IllegalStateException( 364 "Could not parse pre-validated value", e); 365 } 366 } 367 } 368 return ret; 369 } 370 371 @SuppressWarnings("unchecked") 372 private <T> ClassNode<T> parseDefaultImplementation(final ClassNode<T> cn) { 373 if (cn.getDefaultImplementation() != null) { 374 try { 375 return (ClassNode<T>) javaNamespace.getNode(cn.getDefaultImplementation()); 376 } catch (ClassCastException | NameResolutionException e) { 377 throw new IllegalStateException("After validation, " + cn + " had a bad default implementation named " + 378 cn.getDefaultImplementation(), e); 379 } 380 } else { 381 return null; 382 } 383 } 384 385 @SuppressWarnings({"unchecked"}) 386 private <T> void buildInjectionPlan(final Node n, 387 final Map<Node, InjectionPlan<?>> memo) { 388 if (memo.containsKey(n)) { 389 if (BUILDING == memo.get(n)) { 390 final StringBuilder loopyList = new StringBuilder("["); 391 for (final Map.Entry<Node, InjectionPlan<?>> node : memo.entrySet()) { 392 if (node.getValue() == BUILDING) { 393 loopyList.append(" ").append(node.getKey().getFullName()); 394 } 395 } 396 loopyList.append(" ]"); 397 throw new ClassHierarchyException("Detected loopy constructor involving " 398 + loopyList.toString()); 399 } else { 400 return; 401 } 402 } 403 memo.put(n, BUILDING); 404 final InjectionPlan<T> ip; 405 if (n instanceof NamedParameterNode) { 406 final NamedParameterNode<T> np = (NamedParameterNode<T>) n; 407 408 final T boundInstance = parseBoundNamedParameter(np); 409 final T defaultInstance = javaNamespace.parseDefaultValue(np); 410 final T instance = boundInstance != null ? boundInstance : defaultInstance; 411 412 if (instance instanceof Node) { 413 buildInjectionPlan((Node) instance, memo); 414 ip = new Subplan<T>(n, 0, (InjectionPlan<T>) memo.get(instance)); 415 } else if (instance instanceof Set) { 416 final Set<T> entries = (Set<T>) instance; 417 final Set<InjectionPlan<T>> plans = new MonotonicHashSet<>(); 418 for (final T entry : entries) { 419 if (entry instanceof ClassNode) { 420 buildInjectionPlan((ClassNode<?>) entry, memo); 421 plans.add((InjectionPlan<T>) memo.get(entry)); 422 } else { 423 plans.add(new JavaInstance<T>(n, entry)); 424 } 425 426 } 427 ip = new SetInjectionPlan<T>(n, plans); 428 } else if (instance instanceof List) { 429 final List<T> entries = (List<T>) instance; 430 final List<InjectionPlan<T>> plans = new ArrayList<>(); 431 for (final T entry : entries) { 432 if (entry instanceof ClassNode) { 433 buildInjectionPlan((ClassNode<?>) entry, memo); 434 plans.add((InjectionPlan<T>) memo.get(entry)); 435 } else { 436 plans.add(new JavaInstance<T>(n, entry)); 437 } 438 } 439 ip = new ListInjectionPlan<T>(n, plans); 440 } else { 441 ip = new JavaInstance<T>(np, instance); 442 } 443 } else if (n instanceof ClassNode) { 444 final ClassNode<T> cn = (ClassNode<T>) n; 445 446 // Any (or all) of the next four values might be null; that's fine. 447 final T cached = getCachedInstance(cn); 448 final ClassNode<T> boundImpl = c.getBoundImplementation(cn); 449 final ClassNode<T> defaultImpl = parseDefaultImplementation(cn); 450 final ClassNode<ExternalConstructor<T>> ec = c.getBoundConstructor(cn); 451 452 ip = buildClassNodeInjectionPlan(cn, cached, ec, boundImpl, defaultImpl, memo); 453 } else if (n instanceof PackageNode) { 454 throw new IllegalArgumentException( 455 "Request to instantiate Java package as object"); 456 } else { 457 throw new IllegalStateException( 458 "Type hierarchy contained unknown node type!:" + n); 459 } 460 memo.put(n, ip); 461 } 462 463 /** 464 * Return an injection plan for the given class / parameter name. 465 * 466 * @param n The name of an injectable class or interface, or a NamedParameter. 467 * @return 468 * @throws NameResolutionException 469 */ 470 public InjectionPlan<?> getInjectionPlan(final Node n) { 471 final Map<Node, InjectionPlan<?>> memo = new HashMap<>(); 472 buildInjectionPlan(n, memo); 473 return memo.get(n); 474 } 475 476 @Override 477 public InjectionPlan<?> getInjectionPlan(final String name) throws NameResolutionException { 478 return getInjectionPlan(namespace.getNode(name)); 479 } 480 481 @SuppressWarnings("unchecked") 482 public <T> InjectionPlan<T> getInjectionPlan(final Class<T> name) { 483 return (InjectionPlan<T>) getInjectionPlan(javaNamespace.getNode(name)); 484 } 485 486 @Override 487 public boolean isInjectable(final String name) throws NameResolutionException { 488 return getInjectionPlan(namespace.getNode(name)).isInjectable(); 489 } 490 491 @Override 492 public boolean isInjectable(final Class<?> clazz) { 493 try { 494 return isInjectable(ReflectionUtilities.getFullName(clazz)); 495 } catch (final NameResolutionException e) { 496 throw new IllegalStateException("Could not round trip " + clazz + " through ClassHierarchy", e); 497 } 498 } 499 500 @Override 501 public boolean isParameterSet(final String name) throws NameResolutionException { 502 final InjectionPlan<?> p = getInjectionPlan(namespace.getNode(name)); 503 return p.isInjectable(); 504 } 505 506 @Override 507 public boolean isParameterSet(final Class<? extends Name<?>> name) 508 throws BindException { 509 return isParameterSet(name.getName()); 510 } 511 512 private <U> U getInstance(final Node n) throws InjectionException { 513 assertNotConcurrent(); 514 @SuppressWarnings("unchecked") final InjectionPlan<U> plan = (InjectionPlan<U>) getInjectionPlan(n); 515 final U u = (U) injectFromPlan(plan); 516 517 while (!pendingFutures.isEmpty()) { 518 final Iterator<InjectionFuture<?>> i = pendingFutures.iterator(); 519 final InjectionFuture<?> f = i.next(); 520 pendingFutures.remove(f); 521 f.get(); 522 } 523 return u; 524 } 525 526 @Override 527 public <U> U getInstance(final Class<U> clazz) throws InjectionException { 528 if (Name.class.isAssignableFrom(clazz)) { 529 throw new InjectionException("getInstance() called on Name " 530 + ReflectionUtilities.getFullName(clazz) 531 + " Did you mean to call getNamedInstance() instead?"); 532 } 533 return getInstance(javaNamespace.getNode(clazz)); 534 } 535 536 @SuppressWarnings("unchecked") 537 @Override 538 public <U> U getInstance(final String clazz) throws InjectionException, NameResolutionException { 539 return (U) getInstance(namespace.getNode(clazz)); 540 } 541 542 @Override 543 @SuppressWarnings("unchecked") 544 public <T> T getNamedInstance(final Class<? extends Name<T>> clazz) 545 throws InjectionException { 546 return (T) getInstance(javaNamespace.getNode(clazz)); 547 } 548 549 public <T> T getNamedParameter(final Class<? extends Name<T>> clazz) 550 throws InjectionException { 551 return getNamedInstance(clazz); 552 } 553 554 private <T> java.lang.reflect.Constructor<T> getConstructor( 555 final ConstructorDef<T> constructor) throws ClassNotFoundException, 556 NoSuchMethodException, SecurityException { 557 @SuppressWarnings("unchecked") final Class<T> clazz = 558 (Class<T>) javaNamespace.classForName(constructor.getClassName()); 559 final ConstructorArg[] args = constructor.getArgs(); 560 final Class<?>[] parameterTypes = new Class[args.length]; 561 for (int i = 0; i < args.length; i++) { 562 if (args[i].isInjectionFuture()) { 563 parameterTypes[i] = InjectionFuture.class; 564 } else { 565 parameterTypes[i] = javaNamespace.classForName(args[i].getType()); 566 } 567 } 568 final java.lang.reflect.Constructor<T> cons = clazz 569 .getDeclaredConstructor(parameterTypes); 570 cons.setAccessible(true); 571 return cons; 572 } 573 574 /** 575 * This gets really nasty now that constructors can invoke operations on us. 576 * The upshot is that we should check to see if instances have been 577 * registered by callees after each recursive invocation of injectFromPlan or 578 * constructor invocations. The error handling currently bails if the thing we 579 * just instantiated should be discarded. 580 * <p> 581 * This could happen if (for instance), a constructor did a 582 * bindVolatileInstance of its own class to an instance, or somehow triggered 583 * an injection of itself with a different plan (an injection of itself with 584 * the same plan would lead to an infinite recursion, so it's not really our 585 * problem). 586 * 587 * @param plan 588 * @return 589 * @throws InjectionException 590 */ 591 @SuppressWarnings("unchecked") 592 private <T> T injectFromPlan(final InjectionPlan<T> plan) throws InjectionException { 593 594 if (!plan.isFeasible()) { 595 throw new InjectionException("Cannot inject " + plan.getNode().getFullName() + ": " 596 + plan.toCantInjectString()); 597 } 598 if (plan.isAmbiguous()) { 599 throw new InjectionException("Cannot inject " + plan.getNode().getFullName() + " " 600 + plan.toCantInjectString()); 601 } 602 if (plan instanceof InjectionFuturePlan) { 603 final InjectionFuturePlan<T> fut = (InjectionFuturePlan<T>) plan; 604 final String key = fut.getNode().getFullName(); 605 try { 606 final InjectionFuture<?> ret = new InjectionFuture<>( 607 this, javaNamespace.classForName(fut.getNode().getFullName())); 608 pendingFutures.add(ret); 609 return (T) ret; 610 } catch (final ClassNotFoundException e) { 611 throw new InjectionException("Could not get class for " + key, e); 612 } 613 } else if (plan.getNode() instanceof ClassNode && null != getCachedInstance((ClassNode<T>) plan.getNode())) { 614 return getCachedInstance((ClassNode<T>) plan.getNode()); 615 } else if (plan instanceof JavaInstance) { 616 // TODO: Must be named parameter node. Check. 617// throw new IllegalStateException("Instance from plan not in Injector's set of instances?!?"); 618 return ((JavaInstance<T>) plan).getInstance(); 619 } else if (plan instanceof Constructor) { 620 final Constructor<T> constructor = (Constructor<T>) plan; 621 final Object[] args = new Object[constructor.getArgs().length]; 622 final InjectionPlan<?>[] argPlans = constructor.getArgs(); 623 624 for (int i = 0; i < argPlans.length; i++) { 625 args[i] = injectFromPlan(argPlans[i]); 626 } 627 try { 628 concurrentModificationGuard = true; 629 T ret; 630 try { 631 final ConstructorDef<T> def = constructor.getConstructorDef(); 632 final java.lang.reflect.Constructor<T> construct = getConstructor(def); 633 634 if (aspect != null) { 635 ret = aspect.inject(def, construct, args); 636 } else { 637 ret = construct.newInstance(args); 638 } 639 } catch (final IllegalArgumentException e) { 640 final StringBuilder sb = new StringBuilder("Internal Tang error? Could not call constructor " + 641 constructor.getConstructorDef() + " with arguments ["); 642 for (final Object o : args) { 643 sb.append("\n\t" + o); 644 } 645 sb.append("]"); 646 throw new IllegalStateException(sb.toString(), e); 647 } 648 if (ret instanceof ExternalConstructor) { 649 ret = ((ExternalConstructor<T>) ret).newInstance(); 650 } 651 instances.put(constructor.getNode(), ret); 652 return ret; 653 } catch (final ReflectiveOperationException e) { 654 throw new InjectionException("Could not invoke constructor: " + plan, 655 e instanceof InvocationTargetException ? e.getCause() : e); 656 } finally { 657 concurrentModificationGuard = false; 658 } 659 } else if (plan instanceof Subplan) { 660 final Subplan<T> ambiguous = (Subplan<T>) plan; 661 return injectFromPlan(ambiguous.getDelegatedPlan()); 662 } else if (plan instanceof SetInjectionPlan) { 663 final SetInjectionPlan<T> setPlan = (SetInjectionPlan<T>) plan; 664 final Set<T> ret = new MonotonicHashSet<>(); 665 for (final InjectionPlan<T> subplan : setPlan.getEntryPlans()) { 666 ret.add(injectFromPlan(subplan)); 667 } 668 return (T) ret; 669 } else if (plan instanceof ListInjectionPlan) { 670 final ListInjectionPlan<T> listPlan = (ListInjectionPlan<T>) plan; 671 final List<T> ret = new ArrayList<>(); 672 for (final InjectionPlan<T> subplan : listPlan.getEntryPlans()) { 673 ret.add(injectFromPlan(subplan)); 674 } 675 return (T) ret; 676 } else { 677 throw new IllegalStateException("Unknown plan type: " + plan); 678 } 679 } 680 681 @Override 682 public <T> void bindVolatileInstance(final Class<T> cl, final T o) throws BindException { 683 bindVolatileInstanceNoCopy(cl, o); 684 } 685 686 @Override 687 public <T> void bindVolatileParameter(final Class<? extends Name<T>> cl, final T o) 688 throws BindException { 689 bindVolatileParameterNoCopy(cl, o); 690 } 691 692 <T> void bindVolatileInstanceNoCopy(final Class<T> cl, final T o) throws BindException { 693 assertNotConcurrent(); 694 final Node n = javaNamespace.getNode(cl); 695 if (n instanceof ClassNode) { 696 final ClassNode<?> cn = (ClassNode<?>) n; 697 final Object old = getCachedInstance(cn); 698 if (old != null) { 699 throw new BindException("Attempt to re-bind instance. Old value was " 700 + old + " new value is " + o); 701 } 702 instances.put(cn, o); 703 } else { 704 throw new IllegalArgumentException("Expected Class but got " + cl 705 + " (probably a named parameter)."); 706 } 707 } 708 709 <T> void bindVolatileParameterNoCopy(final Class<? extends Name<T>> cl, final T o) 710 throws BindException { 711 final Node n = javaNamespace.getNode(cl); 712 if (n instanceof NamedParameterNode) { 713 final NamedParameterNode<?> np = (NamedParameterNode<?>) n; 714 final Object old = this.c.getNamedParameter(np); 715 if (old != null) { 716 // XXX need to get the binding site here! 717 throw new BindException( 718 "Attempt to re-bind named parameter " + ReflectionUtilities.getFullName(cl) + ". Old value was [" + old 719 + "] new value is [" + o + "]"); 720 } 721 try { 722 namedParameterInstances.put(np, o); 723 } catch (final IllegalArgumentException e) { 724 throw new BindException( 725 "Attempt to bind named parameter " + ReflectionUtilities.getFullName(cl) + " failed. " 726 + "Value is [" + o + "]", e); 727 728 } 729 } else { 730 throw new IllegalArgumentException("Expected Name, got " + cl 731 + " (probably a class)"); 732 } 733 } 734 735 @Override 736 public Injector forkInjector() { 737 try { 738 return forkInjector(new Configuration[0]); 739 } catch (final BindException e) { 740 throw new IllegalStateException(e); 741 } 742 } 743 744 @Override 745 public Injector forkInjector(final Configuration... configurations) 746 throws BindException { 747 final InjectorImpl ret; 748 ret = copy(this, configurations); 749 return ret; 750 } 751 752 @Override 753 public <T> void bindAspect(final Aspect a) throws BindException { 754 if (aspect != null) { 755 throw new BindException("Attempt to re-bind aspect! old=" + aspect + " new=" + a); 756 } 757 aspect = a; 758 } 759 760 @Override 761 public Aspect getAspect() { 762 return aspect; 763 } 764}