You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@reef.apache.org by we...@apache.org on 2015/01/23 00:46:46 UTC
[13/51] [partial] incubator-reef git commit: [REEF-93] Move java
sources to lang/java
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/InjectorImpl.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/InjectorImpl.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/InjectorImpl.java
new file mode 100644
index 0000000..16ce791
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/InjectorImpl.java
@@ -0,0 +1,760 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.reef.tang.implementation.java;
+
+import org.apache.reef.tang.*;
+import org.apache.reef.tang.annotations.Name;
+import org.apache.reef.tang.exceptions.*;
+import org.apache.reef.tang.implementation.*;
+import org.apache.reef.tang.types.*;
+import org.apache.reef.tang.util.MonotonicHashSet;
+import org.apache.reef.tang.util.MonotonicSet;
+import org.apache.reef.tang.util.ReflectionUtilities;
+import org.apache.reef.tang.util.TracingMonotonicTreeMap;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.*;
+
+public class InjectorImpl implements Injector {
+ static final InjectionPlan<?> BUILDING = new InjectionPlan<Object>(null) {
+ @Override
+ public int getNumAlternatives() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String toString() {
+ return "BUILDING INJECTION PLAN";
+ }
+
+ @Override
+ public boolean isAmbiguous() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isInjectable() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected String toAmbiguousInjectString() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected String toInfeasibleInjectString() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected boolean isInfeasibleLeaf() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String toShallowString() {
+ throw new UnsupportedOperationException();
+ }
+
+ };
+ final Map<ClassNode<?>, Object> instances = new TracingMonotonicTreeMap<>();
+ final Map<NamedParameterNode<?>, Object> namedParameterInstances = new TracingMonotonicTreeMap<>();
+ private final Configuration c;
+ private final ClassHierarchy namespace;
+ private final JavaClassHierarchy javaNamespace;
+ private final Set<InjectionFuture<?>> pendingFutures = new HashSet<>();
+ private boolean concurrentModificationGuard = false;
+ private Aspect aspect;
+
+ public InjectorImpl(Configuration c) throws BindException {
+ this.c = c;
+ this.namespace = c.getClassHierarchy();
+ this.javaNamespace = (ClassHierarchyImpl) this.namespace;
+ }
+
+ private static InjectorImpl copy(InjectorImpl old,
+ Configuration... configurations) throws BindException {
+ final InjectorImpl i;
+ try {
+ final ConfigurationBuilder cb = old.c.newBuilder();
+ for (Configuration c : configurations) {
+ cb.addConfiguration(c);
+ }
+ i = new InjectorImpl(cb.build());
+ } catch (BindException e) {
+ throw new IllegalStateException(
+ "Unexpected error copying configuration!", e);
+ }
+ for (ClassNode<?> cn : old.instances.keySet()) {
+ if (cn.getFullName().equals(ReflectionUtilities.getFullName(Injector.class))
+ || cn.getFullName().equals(ReflectionUtilities.getFullName(InjectorImpl.class))) {
+ // This would imply that we're treating injector as a singleton somewhere. It should be copied fresh each time.
+ throw new IllegalStateException();
+ }
+ try {
+ ClassNode<?> new_cn = (ClassNode<?>) i.namespace.getNode(cn
+ .getFullName());
+ i.instances.put(new_cn, old.instances.get(cn));
+ } catch (BindException e) {
+ throw new IllegalStateException("Could not resolve name "
+ + cn.getFullName() + " when copying injector");
+ }
+ }
+ // Copy references to the remaining (which must have been set with
+ // bindVolatileParameter())
+ for (NamedParameterNode<?> np : old.namedParameterInstances.keySet()) {
+ // if (!builder.namedParameters.containsKey(np)) {
+ Object o = old.namedParameterInstances.get(np);
+ NamedParameterNode<?> new_np = (NamedParameterNode<?>) i.namespace
+ .getNode(np.getFullName());
+ i.namedParameterInstances.put(new_np, o);
+ }
+ // Fork the aspect (if any)
+ if (old.aspect != null) {
+ i.bindAspect(old.aspect.createChildAspect());
+ }
+ return i;
+ }
+
+ private void assertNotConcurrent() {
+ if (concurrentModificationGuard) {
+ throw new ConcurrentModificationException("Detected attempt to use Injector from within an injected constructor!");
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> T getCachedInstance(ClassNode<T> cn) {
+ if (cn.getFullName().equals("org.apache.reef.tang.Injector")) {
+ return (T) this;// TODO: We should be insisting on injection futures here! .forkInjector();
+ } else {
+ T t = (T) instances.get(cn);
+ if (t instanceof InjectionFuture) {
+ throw new IllegalStateException("Found an injection future in getCachedInstance: " + cn);
+ }
+ return t;
+ }
+ }
+
+ /**
+ * Produce a list of "interesting" constructors from a set of ClassNodes.
+ * <p/>
+ * Tang Constructors expose a isMoreSpecificThan function that embeds all
+ * a skyline query over the lattices. Precisely:
+ * <p/>
+ * Let candidateConstructors be the union of all constructors defined by
+ * ClassNodes in candidateImplementations.
+ * <p/>
+ * This function returns a set called filteredImplementations, defined as
+ * follows:
+ * <p/>
+ * For each member f of filteredConstructors, there does not exist
+ * a g in candidateConstructors s.t. g.isMoreSpecificThan(f).
+ */
+ private <T> List<InjectionPlan<T>> filterCandidateConstructors(
+ final List<ClassNode<T>> candidateImplementations,
+ final Map<Node, InjectionPlan<?>> memo) {
+
+ final List<InjectionPlan<T>> sub_ips = new ArrayList<>();
+ for (final ClassNode<T> thisCN : candidateImplementations) {
+ final List<Constructor<T>> constructors = new ArrayList<>();
+ final List<ConstructorDef<T>> constructorList = new ArrayList<>();
+ if (null != c.getLegacyConstructor(thisCN)) {
+ constructorList.add(c.getLegacyConstructor(thisCN));
+ }
+ constructorList
+ .addAll(Arrays.asList(thisCN.getInjectableConstructors()));
+
+ for (ConstructorDef<T> def : constructorList) {
+ final List<InjectionPlan<?>> args = new ArrayList<InjectionPlan<?>>();
+ final ConstructorArg[] defArgs = def.getArgs();
+
+ for (final ConstructorArg arg : defArgs) {
+ if (!arg.isInjectionFuture()) {
+ try {
+ final Node argNode = namespace.getNode(arg.getName());
+ buildInjectionPlan(argNode, memo);
+ args.add(memo.get(argNode));
+ } catch (NameResolutionException e) {
+ throw new IllegalStateException("Detected unresolvable "
+ + "constructor arg while building injection plan. "
+ + "This should have been caught earlier!", e);
+ }
+ } else {
+ try {
+ args.add(new InjectionFuturePlan<>(namespace.getNode(arg
+ .getName())));
+ } catch (NameResolutionException e) {
+ throw new IllegalStateException("Detected unresolvable "
+ + "constructor arg while building injection plan. "
+ + "This should have been caught earlier!", e);
+ }
+ }
+ }
+ final Constructor<T> constructor = new Constructor<T>(thisCN, def,
+ args.toArray(new InjectionPlan[0]));
+ constructors.add(constructor);
+ }
+ // The constructors are embedded in a lattice defined by
+ // isMoreSpecificThan(). We want to see if, amongst the injectable
+ // plans, there is a unique dominant plan, and select it.
+
+ // First, compute the set of injectable plans.
+ final List<Integer> liveIndices = new ArrayList<>();
+ for (int i = 0; i < constructors.size(); i++) {
+ if (constructors.get(i).getNumAlternatives() > 0) {
+ liveIndices.add(i);
+ }
+ }
+ // Now, do an all-by-all comparison, removing indices that are dominated
+ // by others.
+ for (int i = 0; i < liveIndices.size(); i++) {
+ for (int j = i + 1; j < liveIndices.size(); j++) {
+ final ConstructorDef<T> ci = constructors.get(liveIndices.get(i))
+ .getConstructorDef();
+ final ConstructorDef<T> cj = constructors.get(liveIndices.get(j))
+ .getConstructorDef();
+
+ if (ci.isMoreSpecificThan(cj)) {
+ liveIndices.remove(j);
+ j--;
+ } else if (cj.isMoreSpecificThan(ci)) {
+ liveIndices.remove(i);
+ // Done with this inner loop invocation. Check the new ci.
+ i--;
+ break;
+ }
+ }
+ }
+ if (constructors.size() > 0) {
+ sub_ips.add(wrapInjectionPlans(thisCN, constructors, false,
+ liveIndices.size() == 1 ? liveIndices.get(0) : -1));
+ }
+ }
+ return sub_ips;
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> InjectionPlan<T> buildClassNodeInjectionPlan(ClassNode<T> cn,
+ T cachedInstance,
+ ClassNode<ExternalConstructor<T>> externalConstructor,
+ ClassNode<T> boundImpl,
+ ClassNode<T> defaultImpl,
+ Map<Node, InjectionPlan<?>> memo) {
+
+ if (cachedInstance != null) {
+ return new JavaInstance<T>(cn, cachedInstance);
+ } else if (externalConstructor != null) {
+ buildInjectionPlan(externalConstructor, memo);
+ return new Subplan<>(cn, 0, (InjectionPlan<T>) memo.get(externalConstructor));
+ } else if (boundImpl != null && !cn.equals(boundImpl)) {
+ // We need to delegate to boundImpl, so recurse.
+ buildInjectionPlan(boundImpl, memo);
+ return new Subplan<>(cn, 0, (InjectionPlan<T>) memo.get(boundImpl));
+ } else if (defaultImpl != null && !cn.equals(defaultImpl)) {
+ buildInjectionPlan(defaultImpl, memo);
+ return new Subplan<>(cn, 0, (InjectionPlan<T>) memo.get(defaultImpl));
+ } else {
+ // if we're here and there isn't a bound impl or a default impl,
+ // then we're bound / defaulted to ourselves, so don't add
+ // other impls to the list of things to consider.
+ final List<ClassNode<T>> candidateImplementations = new ArrayList<>();
+ candidateImplementations.add(cn);
+ final List<InjectionPlan<T>> sub_ips = filterCandidateConstructors(candidateImplementations, memo);
+ if (sub_ips.size() == 1) {
+ return wrapInjectionPlans(cn, sub_ips, false, -1);
+ } else {
+ return wrapInjectionPlans(cn, sub_ips, true, -1);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> InjectionPlan<T> wrapInjectionPlans(ClassNode<T> infeasibleNode,
+ List<? extends InjectionPlan<T>> list, boolean forceAmbiguous, int selectedIndex) {
+ if (list.size() == 0) {
+ return new Subplan<>(infeasibleNode);
+ } else if ((!forceAmbiguous) && list.size() == 1) {
+ return list.get(0);
+ } else {
+ return new Subplan<>(infeasibleNode, selectedIndex, list.toArray(new InjectionPlan[0]));
+ }
+ }
+
+ /**
+ * Parse the bound value of np. When possible, this returns a cached instance.
+ *
+ * @return null if np has not been bound.
+ * @throws ParseException
+ */
+ @SuppressWarnings("unchecked")
+ private <T> T parseBoundNamedParameter(NamedParameterNode<T> np) {
+ final T ret;
+
+ @SuppressWarnings("rawtypes")
+ final Set<Object> boundSet = c.getBoundSet((NamedParameterNode) np);
+ if (!boundSet.isEmpty()) {
+ Set<T> ret2 = new MonotonicSet<>();
+ for (Object o : boundSet) {
+ if (o instanceof String) {
+ try {
+ ret2.add(javaNamespace.parse(np, (String) o));
+ } catch (ParseException e) {
+ // Parsability is now pre-checked in bindSet, so it should not be reached!
+ throw new IllegalStateException("Could not parse " + o + " which was passed into " + np + " FIXME: Parsability is not currently checked by bindSetEntry(Node,String)");
+ }
+ } else if (o instanceof Node) {
+ ret2.add((T) o);
+ } else {
+ throw new IllegalStateException("Unexpected object " + o + " in bound set. Should consist of nodes and strings");
+ }
+ }
+ return (T) ret2;
+ }
+ final List<Object> boundList = c.getBoundList((NamedParameterNode) np);
+ if (boundList != null) {
+ List<T> ret2 = new ArrayList<>();
+ for (Object o : boundList) {
+ if (o instanceof String) {
+ try {
+ ret2.add(javaNamespace.parse(np, (String) o));
+ } catch (ParseException e) {
+ // Parsability is now pre-checked in bindList, so it should not be reached!
+ throw new IllegalStateException("Could not parse " + o + " which was passed into " + np + " FIXME: " +
+ "Parsability is not currently checked by bindList(Node,List)");
+ }
+ } else if (o instanceof Node) {
+ ret2.add((T) o);
+ } else {
+ throw new IllegalStateException("Unexpected object " + o + " in bound list. Should consist of nodes and " +
+ "strings");
+ }
+ }
+ return (T) ret2;
+ } else if (namedParameterInstances.containsKey(np)) {
+ ret = (T) namedParameterInstances.get(np);
+ } else {
+ final String value = c.getNamedParameter(np);
+ if (value == null) {
+ ret = null;
+ } else {
+ try {
+ ret = javaNamespace.parse(np, value);
+ namedParameterInstances.put(np, ret);
+ } catch (BindException e) {
+ throw new IllegalStateException(
+ "Could not parse pre-validated value", e);
+ }
+ }
+ }
+ return ret;
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> ClassNode<T> parseDefaultImplementation(ClassNode<T> cn) {
+ if (cn.getDefaultImplementation() != null) {
+ try {
+ return (ClassNode<T>) javaNamespace.getNode(cn.getDefaultImplementation());
+ } catch (ClassCastException | NameResolutionException e) {
+ throw new IllegalStateException("After validation, " + cn + " had a bad default implementation named " + cn.getDefaultImplementation(), e);
+ }
+ } else {
+ return null;
+ }
+ }
+
+ @SuppressWarnings({"unchecked"})
+ private <T> void buildInjectionPlan(final Node n,
+ Map<Node, InjectionPlan<?>> memo) {
+ if (memo.containsKey(n)) {
+ if (BUILDING == memo.get(n)) {
+ StringBuilder loopyList = new StringBuilder("[");
+ for (Node node : memo.keySet()) {
+ if (memo.get(node) == BUILDING) {
+ loopyList.append(" " + node.getFullName());
+ }
+ }
+ loopyList.append(" ]");
+ throw new ClassHierarchyException("Detected loopy constructor involving "
+ + loopyList.toString());
+ } else {
+ return;
+ }
+ }
+ memo.put(n, BUILDING);
+ final InjectionPlan<T> ip;
+ if (n instanceof NamedParameterNode) {
+ final NamedParameterNode<T> np = (NamedParameterNode<T>) n;
+
+ final T boundInstance = parseBoundNamedParameter(np);
+ final T defaultInstance = javaNamespace.parseDefaultValue(np);
+ final T instance = boundInstance != null ? boundInstance : defaultInstance;
+
+ if (instance instanceof Node) {
+ buildInjectionPlan((Node) instance, memo);
+ ip = new Subplan<T>(n, 0, (InjectionPlan<T>) memo.get(instance));
+ } else if (instance instanceof Set) {
+ Set<T> entries = (Set<T>) instance;
+ Set<InjectionPlan<T>> plans = new MonotonicHashSet<>();
+ for (T entry : entries) {
+ if (entry instanceof ClassNode) {
+ buildInjectionPlan((ClassNode<?>) entry, memo);
+ plans.add((InjectionPlan<T>) memo.get(entry));
+ } else {
+ plans.add(new JavaInstance<T>(n, entry));
+ }
+
+ }
+ ip = new SetInjectionPlan<T>(n, plans);
+ } else if (instance instanceof List) {
+ List<T> entries = (List<T>) instance;
+ List<InjectionPlan<T>> plans = new ArrayList<>();
+ for (T entry : entries) {
+ if (entry instanceof ClassNode) {
+ buildInjectionPlan((ClassNode<?>) entry, memo);
+ plans.add((InjectionPlan<T>) memo.get(entry));
+ } else {
+ plans.add(new JavaInstance<T>(n, entry));
+ }
+ }
+ ip = new ListInjectionPlan<T>(n, plans);
+ } else {
+ ip = new JavaInstance<T>(np, instance);
+ }
+ } else if (n instanceof ClassNode) {
+ final ClassNode<T> cn = (ClassNode<T>) n;
+
+ // Any (or all) of the next four values might be null; that's fine.
+ final T cached = getCachedInstance(cn);
+ final ClassNode<T> boundImpl = c.getBoundImplementation(cn);
+ final ClassNode<T> defaultImpl = parseDefaultImplementation(cn);
+ final ClassNode<ExternalConstructor<T>> ec = c.getBoundConstructor(cn);
+
+ ip = buildClassNodeInjectionPlan(cn, cached, ec, boundImpl, defaultImpl, memo);
+ } else if (n instanceof PackageNode) {
+ throw new IllegalArgumentException(
+ "Request to instantiate Java package as object");
+ } else {
+ throw new IllegalStateException(
+ "Type hierarchy contained unknown node type!:" + n);
+ }
+ memo.put(n, ip);
+ }
+
+ /**
+ * Return an injection plan for the given class / parameter name.
+ *
+ * @param n The name of an injectable class or interface, or a NamedParameter.
+ * @return
+ * @throws NameResolutionException
+ */
+ public InjectionPlan<?> getInjectionPlan(final Node n) {
+ Map<Node, InjectionPlan<?>> memo = new HashMap<>();
+ buildInjectionPlan(n, memo);
+ return memo.get(n);
+ }
+
+ @Override
+ public InjectionPlan<?> getInjectionPlan(String name) throws NameResolutionException {
+ return getInjectionPlan(namespace.getNode(name));
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> InjectionPlan<T> getInjectionPlan(Class<T> name) {
+ return (InjectionPlan<T>) getInjectionPlan(javaNamespace.getNode(name));
+ }
+
+ @Override
+ public boolean isInjectable(String name) throws NameResolutionException {
+ return getInjectionPlan(namespace.getNode(name)).isInjectable();
+ }
+
+ @Override
+ public boolean isInjectable(Class<?> clazz) {
+ try {
+ return isInjectable(ReflectionUtilities.getFullName(clazz));
+ } catch (NameResolutionException e) {
+ throw new IllegalStateException("Could not round trip " + clazz + " through ClassHierarchy", e);
+ }
+ }
+
+ @Override
+ public boolean isParameterSet(String name) throws NameResolutionException {
+ InjectionPlan<?> p = getInjectionPlan(namespace.getNode(name));
+ return p.isInjectable();
+ }
+
+ @Override
+ public boolean isParameterSet(Class<? extends Name<?>> name)
+ throws BindException {
+ return isParameterSet(name.getName());
+ }
+
+ private <U> U getInstance(Node n) throws InjectionException {
+ assertNotConcurrent();
+ @SuppressWarnings("unchecked")
+ InjectionPlan<U> plan = (InjectionPlan<U>) getInjectionPlan(n);
+ U u = (U) injectFromPlan(plan);
+
+ while (!pendingFutures.isEmpty()) {
+ Iterator<InjectionFuture<?>> i = pendingFutures.iterator();
+ InjectionFuture<?> f = i.next();
+ pendingFutures.remove(f);
+ f.get();
+ }
+ return u;
+ }
+
+ @Override
+ public <U> U getInstance(Class<U> clazz) throws InjectionException {
+ if (Name.class.isAssignableFrom(clazz)) {
+ throw new InjectionException("getInstance() called on Name "
+ + ReflectionUtilities.getFullName(clazz)
+ + " Did you mean to call getNamedInstance() instead?");
+ }
+ return getInstance(javaNamespace.getNode(clazz));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <U> U getInstance(String clazz) throws InjectionException, NameResolutionException {
+ return (U) getInstance(namespace.getNode(clazz));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getNamedInstance(Class<? extends Name<T>> clazz)
+ throws InjectionException {
+ return (T) getInstance(javaNamespace.getNode(clazz));
+ }
+
+ public <T> T getNamedParameter(Class<? extends Name<T>> clazz)
+ throws InjectionException {
+ return getNamedInstance(clazz);
+ }
+
+ private <T> java.lang.reflect.Constructor<T> getConstructor(
+ ConstructorDef<T> constructor) throws ClassNotFoundException,
+ NoSuchMethodException, SecurityException {
+ @SuppressWarnings("unchecked")
+ Class<T> clazz = (Class<T>) javaNamespace.classForName(constructor
+ .getClassName());
+ ConstructorArg[] args = constructor.getArgs();
+ Class<?> parameterTypes[] = new Class[args.length];
+ for (int i = 0; i < args.length; i++) {
+ if (args[i].isInjectionFuture()) {
+ parameterTypes[i] = InjectionFuture.class;
+ } else {
+ parameterTypes[i] = javaNamespace.classForName(args[i].getType());
+ }
+ }
+ java.lang.reflect.Constructor<T> cons = clazz
+ .getDeclaredConstructor(parameterTypes);
+ cons.setAccessible(true);
+ return cons;
+ }
+
+ /**
+ * This gets really nasty now that constructors can invoke operations on us.
+ * The upshot is that we should check to see if instances have been
+ * registered by callees after each recursive invocation of injectFromPlan or
+ * constructor invocations. The error handling currently bails if the thing we
+ * just instantiated should be discarded.
+ * <p/>
+ * This could happen if (for instance), a constructor did a
+ * bindVolatileInstance of its own class to an instance, or somehow triggered
+ * an injection of itself with a different plan (an injection of itself with
+ * the same plan would lead to an infinite recursion, so it's not really our
+ * problem).
+ *
+ * @param plan
+ * @return
+ * @throws InjectionException
+ */
+ @SuppressWarnings("unchecked")
+ private <T> T injectFromPlan(InjectionPlan<T> plan) throws InjectionException {
+
+ if (!plan.isFeasible()) {
+ throw new InjectionException("Cannot inject " + plan.getNode().getFullName() + ": "
+ + plan.toCantInjectString());
+ }
+ if (plan.isAmbiguous()) {
+ throw new InjectionException("Cannot inject " + plan.getNode().getFullName() + " "
+ + plan.toCantInjectString());
+ }
+ if (plan instanceof InjectionFuturePlan) {
+ InjectionFuturePlan<T> fut = (InjectionFuturePlan<T>) plan;
+ final String key = fut.getNode().getFullName();
+ try {
+ InjectionFuture<?> ret = new InjectionFuture<>(this, javaNamespace.classForName(fut.getNode().getFullName()));
+ pendingFutures.add(ret);
+ return (T) ret;
+ } catch (ClassNotFoundException e) {
+ throw new InjectionException("Could not get class for " + key);
+ }
+ } else if (plan.getNode() instanceof ClassNode && null != getCachedInstance((ClassNode<T>) plan.getNode())) {
+ return getCachedInstance((ClassNode<T>) plan.getNode());
+ } else if (plan instanceof JavaInstance) {
+ // TODO: Must be named parameter node. Check.
+// throw new IllegalStateException("Instance from plan not in Injector's set of instances?!?");
+ return ((JavaInstance<T>) plan).instance;
+ } else if (plan instanceof Constructor) {
+ final Constructor<T> constructor = (Constructor<T>) plan;
+ final Object[] args = new Object[constructor.getArgs().length];
+ final InjectionPlan<?>[] argPlans = constructor.getArgs();
+
+ for (int i = 0; i < argPlans.length; i++) {
+ args[i] = injectFromPlan(argPlans[i]);
+ }
+ try {
+ concurrentModificationGuard = true;
+ T ret;
+ try {
+ ConstructorDef<T> def = (ConstructorDef<T>) constructor.getConstructorDef();
+ java.lang.reflect.Constructor<T> c = getConstructor(def);
+
+ if (aspect != null) {
+ ret = aspect.inject(def, c, args);
+ } else {
+ ret = c.newInstance(args);
+ }
+ } catch (IllegalArgumentException e) {
+ StringBuilder sb = new StringBuilder("Internal Tang error? Could not call constructor " + constructor.getConstructorDef() + " with arguments [");
+ for (Object o : args) {
+ sb.append("\n\t" + o);
+ }
+ sb.append("]");
+ throw new IllegalStateException(sb.toString(), e);
+ }
+ if (ret instanceof ExternalConstructor) {
+ ret = ((ExternalConstructor<T>) ret).newInstance();
+ }
+ instances.put(constructor.getNode(), ret);
+ return ret;
+ } catch (ReflectiveOperationException e) {
+ throw new InjectionException("Could not invoke constructor: " + plan, e instanceof InvocationTargetException ? e.getCause() : e);
+ } finally {
+ concurrentModificationGuard = false;
+ }
+ } else if (plan instanceof Subplan) {
+ Subplan<T> ambiguous = (Subplan<T>) plan;
+ return injectFromPlan(ambiguous.getDelegatedPlan());
+ } else if (plan instanceof SetInjectionPlan) {
+ SetInjectionPlan<T> setPlan = (SetInjectionPlan<T>) plan;
+ Set<T> ret = new MonotonicHashSet<>();
+ for (InjectionPlan<T> subplan : setPlan.getEntryPlans()) {
+ ret.add(injectFromPlan(subplan));
+ }
+ return (T) ret;
+ } else if (plan instanceof ListInjectionPlan) {
+ ListInjectionPlan<T> listPlan = (ListInjectionPlan<T>) plan;
+ List<T> ret = new ArrayList<>();
+ for (InjectionPlan<T> subplan : listPlan.getEntryPlans()) {
+ ret.add(injectFromPlan(subplan));
+ }
+ return (T) ret;
+ } else {
+ throw new IllegalStateException("Unknown plan type: " + plan);
+ }
+ }
+
+ @Override
+ public <T> void bindVolatileInstance(Class<T> c, T o) throws BindException {
+ bindVolatileInstanceNoCopy(c, o);
+ }
+
+ @Override
+ public <T> void bindVolatileParameter(Class<? extends Name<T>> c, T o)
+ throws BindException {
+ bindVolatileParameterNoCopy(c, o);
+ }
+
+ <T> void bindVolatileInstanceNoCopy(Class<T> c, T o) throws BindException {
+ assertNotConcurrent();
+ Node n = javaNamespace.getNode(c);
+ if (n instanceof ClassNode) {
+ ClassNode<?> cn = (ClassNode<?>) n;
+ Object old = getCachedInstance(cn);
+ if (old != null) {
+ throw new BindException("Attempt to re-bind instance. Old value was "
+ + old + " new value is " + o);
+ }
+ instances.put(cn, o);
+ } else {
+ throw new IllegalArgumentException("Expected Class but got " + c
+ + " (probably a named parameter).");
+ }
+ }
+
+ <T> void bindVolatileParameterNoCopy(Class<? extends Name<T>> c, T o)
+ throws BindException {
+ Node n = javaNamespace.getNode(c);
+ if (n instanceof NamedParameterNode) {
+ NamedParameterNode<?> np = (NamedParameterNode<?>) n;
+ Object old = this.c.getNamedParameter(np);
+ if (old != null) {
+ // XXX need to get the binding site here!
+ throw new BindException(
+ "Attempt to re-bind named parameter " + ReflectionUtilities.getFullName(c) + ". Old value was [" + old
+ + "] new value is [" + o + "]");
+ }
+ try {
+ namedParameterInstances.put(np, o);
+ } catch (IllegalArgumentException e) {
+ throw new BindException(
+ "Attempt to re-bind named parameter " + ReflectionUtilities.getFullName(c) + ". Old value was [" + old
+ + "] new value is [" + o + "]");
+
+ }
+ } else {
+ throw new IllegalArgumentException("Expected Name, got " + c
+ + " (probably a class)");
+ }
+ }
+
+ @Override
+ public Injector forkInjector() {
+ try {
+ return forkInjector(new Configuration[0]);
+ } catch (BindException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @Override
+ public Injector forkInjector(Configuration... configurations)
+ throws BindException {
+ InjectorImpl ret;
+ ret = copy(this, configurations);
+ return ret;
+ }
+
+ @Override
+ public <T> void bindAspect(Aspect a) throws BindException {
+ if (aspect != null) {
+ throw new BindException("Attempt to re-bind aspect! old=" + aspect + " new=" + a);
+ }
+ aspect = a;
+ }
+
+ @Override
+ public Aspect getAspect() {
+ return aspect;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/JavaConfigurationBuilderImpl.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/JavaConfigurationBuilderImpl.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/JavaConfigurationBuilderImpl.java
new file mode 100644
index 0000000..d13a014
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/JavaConfigurationBuilderImpl.java
@@ -0,0 +1,238 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.reef.tang.implementation.java;
+
+import org.apache.reef.tang.Configuration;
+import org.apache.reef.tang.ExternalConstructor;
+import org.apache.reef.tang.JavaClassHierarchy;
+import org.apache.reef.tang.JavaConfigurationBuilder;
+import org.apache.reef.tang.annotations.Name;
+import org.apache.reef.tang.exceptions.BindException;
+import org.apache.reef.tang.implementation.ConfigurationBuilderImpl;
+import org.apache.reef.tang.implementation.ConfigurationImpl;
+import org.apache.reef.tang.types.ClassNode;
+import org.apache.reef.tang.types.NamedParameterNode;
+import org.apache.reef.tang.types.Node;
+import org.apache.reef.tang.util.ReflectionUtilities;
+
+import java.lang.reflect.Type;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class JavaConfigurationBuilderImpl extends ConfigurationBuilderImpl
+ implements JavaConfigurationBuilder {
+
+ public JavaConfigurationBuilderImpl(URL[] jars, Configuration[] confs, Class<? extends ExternalConstructor<?>>[] parsers)
+ throws BindException {
+ super(jars, confs, parsers);
+ }
+
+ JavaConfigurationBuilderImpl() {
+ super();
+ }
+
+ public JavaConfigurationBuilderImpl(URL[] jars) throws BindException {
+ super(jars);
+ }
+
+ JavaConfigurationBuilderImpl(JavaConfigurationBuilderImpl impl) {
+ super(impl);
+ }
+
+ public JavaConfigurationBuilderImpl(Configuration[] confs)
+ throws BindException {
+ super(confs);
+ }
+
+ @Override
+ public ConfigurationImpl build() {
+ return new JavaConfigurationImpl(new JavaConfigurationBuilderImpl(this));
+ }
+
+ private Node getNode(Class<?> c) {
+ return ((JavaClassHierarchy) namespace).getNode(c);
+ }
+
+ @Override
+ public <T> JavaConfigurationBuilder bind(Class<T> c, Class<?> val) throws BindException {
+ super.bind(getNode(c), getNode(val));
+ return this;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> JavaConfigurationBuilder bindImplementation(Class<T> c, Class<? extends T> d)
+ throws BindException {
+ final Node cn = getNode(c);
+ final Node dn = getNode(d);
+ if (!(cn instanceof ClassNode)) {
+ throw new BindException(
+ "bindImplementation passed interface that resolved to " + cn
+ + " expected a ClassNode<?>");
+ }
+ if (!(dn instanceof ClassNode)) {
+ throw new BindException(
+ "bindImplementation passed implementation that resolved to " + dn
+ + " expected a ClassNode<?>");
+ }
+ super.bindImplementation((ClassNode<T>) cn, (ClassNode<? extends T>) dn);
+ return this;
+ }
+
+ @Override
+ public JavaConfigurationBuilder bindNamedParameter(final Class<? extends Name<?>> name, final String value)
+ throws BindException {
+ if (value == null) {
+ throw new IllegalStateException("The value null set to the named parameter is illegal: " + name);
+ }
+ final Node np = getNode(name);
+ if (np instanceof NamedParameterNode) {
+ super.bindParameter((NamedParameterNode<?>) np, value);
+ return this;
+ } else {
+ throw new BindException(
+ "Detected type mismatch when setting named parameter " + name
+ + " Expected NamedParameterNode, but namespace contains a " + np);
+ }
+ }
+
+ @Override
+ public <T> JavaConfigurationBuilder bindNamedParameter(Class<? extends Name<T>> iface,
+ Class<? extends T> impl) throws BindException {
+ Node ifaceN = getNode(iface);
+ Node implN = getNode(impl);
+ if (!(ifaceN instanceof NamedParameterNode)) {
+ throw new BindException("Type mismatch when setting named parameter " + ifaceN
+ + " Expected NamedParameterNode");
+ }
+ bind(ifaceN, implN);
+ return this;
+ }
+
+ @SuppressWarnings({"unchecked"})
+ public <T> JavaConfigurationBuilder bindConstructor(Class<T> c,
+ Class<? extends ExternalConstructor<? extends T>> v) throws BindException {
+ final Node n = getNode(c);
+ final Node m = getNode(v);
+ if (!(n instanceof ClassNode)) {
+ throw new BindException("BindConstructor got class that resolved to " + n + "; expected ClassNode");
+ }
+ if (!(m instanceof ClassNode)) {
+ throw new BindException("BindConstructor got class that resolved to " + m + "; expected ClassNode");
+ }
+ super.bindConstructor((ClassNode<T>) n, (ClassNode<? extends ExternalConstructor<? extends T>>) m);
+ return this;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> JavaConfigurationBuilder bindSetEntry(Class<? extends Name<Set<T>>> iface, String value) throws BindException {
+ final Node n = getNode(iface);
+
+ if (!(n instanceof NamedParameterNode)) {
+ throw new BindException("BindSetEntry got an interface that resolved to " + n + "; expected a NamedParameter");
+ }
+ final Type setType = ReflectionUtilities.getInterfaceTarget(Name.class, iface);
+ if (!ReflectionUtilities.getRawClass(setType).equals(Set.class)) {
+ throw new BindException("BindSetEntry got a NamedParameter that takes a " + setType + "; expected Set<...>");
+ }
+// Type valType = ReflectionUtilities.getInterfaceTarget(Set.class, setType);
+ super.bindSetEntry((NamedParameterNode<Set<T>>) n, value);
+ return this;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> JavaConfigurationBuilder bindSetEntry(Class<? extends Name<Set<T>>> iface, Class<? extends T> impl) throws BindException {
+ final Node n = getNode(iface);
+ final Node m = getNode(impl);
+
+ if (!(n instanceof NamedParameterNode)) {
+ throw new BindException("BindSetEntry got an interface that resolved to " + n + "; expected a NamedParameter");
+ }
+ final Type setType = ReflectionUtilities.getInterfaceTarget(Name.class, iface);
+ if (!ReflectionUtilities.getRawClass(setType).equals(Set.class)) {
+ throw new BindException("BindSetEntry got a NamedParameter that takes a " + setType + "; expected Set<...>");
+ }
+ final Type valType = ReflectionUtilities.getInterfaceTarget(Set.class, setType);
+ if (!ReflectionUtilities.getRawClass(valType).isAssignableFrom(impl)) {
+ throw new BindException("BindSetEntry got implementation " + impl + " that is incompatible with expected type " + valType);
+ }
+
+ super.bindSetEntry((NamedParameterNode<Set<T>>) n, m);
+ return this;
+ }
+
+ /**
+ * Binding list method for JavaConfigurationBuilder. It checks the type of a given named parameter,
+ * and whether all the list's elements can be applied to the named parameter. The elements' type
+ * should be either java Class or String.
+ * <p/>
+ * It does not check whether the list's String values can be parsed to T, like bindSetEntry.
+ *
+ * @param iface target named parameter to be instantiated
+ * @param implList implementation list used to instantiate the named parameter
+ * @param <T> type of the list
+ * @return bound JavaConfigurationBuilder object
+ * @throws BindException
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> JavaConfigurationBuilder bindList(Class<? extends Name<List<T>>> iface, List implList)
+ throws BindException {
+ final Node n = getNode(iface);
+ List<Object> result = new ArrayList<>();
+
+ if (!(n instanceof NamedParameterNode)) {
+ throw new BindException("BindList got an interface that resolved to " + n + "; expected a NamedParameter");
+ }
+ final Type listType = ReflectionUtilities.getInterfaceTarget(Name.class, iface);
+ if (!ReflectionUtilities.getRawClass(listType).equals(List.class)) {
+ throw new BindException("BindList got a NamedParameter that takes a " + listType + "; expected List<...>");
+ }
+ if (!implList.isEmpty()) {
+ final Type valType = ReflectionUtilities.getInterfaceTarget(List.class, listType);
+ for (Object item : implList) {
+ if (item instanceof Class) {
+ if (!ReflectionUtilities.getRawClass(valType).isAssignableFrom((Class) item)) {
+ throw new BindException("BindList got a list element which is not assignable to the given Type; " +
+ "expected: " + valType);
+ }
+ result.add(getNode((Class) item));
+ } else if (item instanceof String) {
+ result.add(item);
+ } else {
+ throw new BindException("BindList got an list element with unsupported type; expected Class or String " +
+ "object");
+ }
+ }
+ }
+
+ super.bindList((NamedParameterNode<List<T>>) n, result);
+ return this;
+ }
+
+ private class JavaConfigurationImpl extends ConfigurationImpl {
+ JavaConfigurationImpl(JavaConfigurationBuilderImpl builder) {
+ super(builder);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/JavaInstance.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/JavaInstance.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/JavaInstance.java
new file mode 100644
index 0000000..2d1a21c
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/JavaInstance.java
@@ -0,0 +1,75 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.reef.tang.implementation.java;
+
+import org.apache.reef.tang.implementation.InjectionPlan;
+import org.apache.reef.tang.types.Node;
+
+final public class JavaInstance<T> extends InjectionPlan<T> {
+ final T instance;
+
+ public JavaInstance(Node name, T instance) {
+ super(name);
+ this.instance = instance;
+ }
+
+ @Override
+ public int getNumAlternatives() {
+ return instance == null ? 0 : 1;
+ }
+
+ @Override
+ public String toString() {
+ return getNode() + " = " + instance;
+ }
+
+ @Override
+ public boolean isAmbiguous() {
+ return false;
+ }
+
+ @Override
+ public boolean isInjectable() {
+ return instance != null;
+ }
+
+ public String getInstanceAsString() {
+ return instance.toString();
+ }
+
+ @Override
+ protected String toAmbiguousInjectString() {
+ throw new IllegalArgumentException("toAmbiguousInjectString called on JavaInstance!" + this.toString());
+ }
+
+ @Override
+ protected String toInfeasibleInjectString() {
+ return getNode() + " is not bound.";
+ }
+
+ @Override
+ protected boolean isInfeasibleLeaf() {
+ return true;
+ }
+
+ @Override
+ public String toShallowString() {
+ return toString();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/JavaNodeFactory.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/JavaNodeFactory.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/JavaNodeFactory.java
new file mode 100644
index 0000000..f727c89
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/JavaNodeFactory.java
@@ -0,0 +1,333 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.reef.tang.implementation.java;
+
+import org.apache.reef.tang.ExternalConstructor;
+import org.apache.reef.tang.InjectionFuture;
+import org.apache.reef.tang.annotations.*;
+import org.apache.reef.tang.exceptions.ClassHierarchyException;
+import org.apache.reef.tang.implementation.types.*;
+import org.apache.reef.tang.types.*;
+import org.apache.reef.tang.util.MonotonicSet;
+import org.apache.reef.tang.util.ReflectionUtilities;
+
+import javax.inject.Inject;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+public class JavaNodeFactory {
+
+ @SuppressWarnings("unchecked")
+ static <T> ClassNodeImpl<T> createClassNode(Node parent, Class<T> clazz) throws ClassHierarchyException {
+ final boolean injectable;
+ final boolean unit = clazz.isAnnotationPresent(Unit.class);
+ final String simpleName = ReflectionUtilities.getSimpleName(clazz);
+ final String fullName = ReflectionUtilities.getFullName(clazz);
+ final boolean isStatic = Modifier.isStatic(clazz.getModifiers());
+ final boolean parentIsUnit = ((parent instanceof ClassNode) && !isStatic) ?
+ ((ClassNode<?>) parent).isUnit() : false;
+
+ if (clazz.isLocalClass() || clazz.isMemberClass()) {
+ if (!isStatic) {
+ if (parent instanceof ClassNode) {
+ injectable = ((ClassNode<?>) parent).isUnit();
+ } else {
+ injectable = false;
+ }
+ } else {
+ injectable = true;
+ }
+ } else {
+ injectable = true;
+ }
+
+ boolean foundNonStaticInnerClass = false;
+ for (Class<?> c : clazz.getDeclaredClasses()) {
+ if (!Modifier.isStatic(c.getModifiers())) {
+ foundNonStaticInnerClass = true;
+ }
+ }
+
+ if (unit && !foundNonStaticInnerClass) {
+ throw new ClassHierarchyException("Class " + ReflectionUtilities.getFullName(clazz) + " has an @Unit annotation, but no non-static inner classes. Such @Unit annotations would have no effect, and are therefore disallowed.");
+ }
+
+ Constructor<T>[] constructors = (Constructor<T>[]) clazz
+ .getDeclaredConstructors();
+ MonotonicSet<ConstructorDef<T>> injectableConstructors = new MonotonicSet<>();
+ ArrayList<ConstructorDef<T>> allConstructors = new ArrayList<>();
+ for (int k = 0; k < constructors.length; k++) {
+ boolean constructorAnnotatedInjectable = (constructors[k]
+ .getAnnotation(Inject.class) != null);
+ if (constructorAnnotatedInjectable && constructors[k].isSynthetic()) {
+ // Not sure if we *can* unit test this one.
+ throw new ClassHierarchyException(
+ "Synthetic constructor was annotated with @Inject!");
+ }
+ if (parentIsUnit && (constructorAnnotatedInjectable || constructors[k].getParameterTypes().length != 1)) {
+ throw new ClassHierarchyException(
+ "Detected explicit constructor in class enclosed in @Unit " + fullName + " Such constructors are disallowed.");
+ }
+ boolean constructorInjectable = constructorAnnotatedInjectable || parentIsUnit;
+ // ConstructorDef's constructor checks for duplicate
+ // parameters
+ // The injectableConstructors set checks for ambiguous
+ // boundConstructors.
+ ConstructorDef<T> def = JavaNodeFactory.createConstructorDef(injectable,
+ constructors[k], constructorAnnotatedInjectable);
+ if (constructorInjectable) {
+ if (injectableConstructors.contains(def)) {
+ throw new ClassHierarchyException(
+ "Ambiguous boundConstructors detected in class " + clazz + ": "
+ + def + " differs from some other" + " constructor only "
+ + "by parameter order.");
+ } else {
+ injectableConstructors.add(def);
+ }
+ }
+ allConstructors.add(def);
+ }
+ final String defaultImplementation;
+ if (clazz.isAnnotationPresent(DefaultImplementation.class)) {
+ DefaultImplementation defaultImpl
+ = clazz.getAnnotation(DefaultImplementation.class);
+ Class<?> defaultImplementationClazz = defaultImpl.value();
+ if (defaultImplementationClazz.equals(Void.class)) {
+ defaultImplementation = defaultImpl.name();
+ // XXX check isAssignableFrom, other type problems here.
+ } else {
+ if (!clazz.isAssignableFrom(defaultImplementationClazz)) {
+ throw new ClassHierarchyException(clazz
+ + " declares its default implementation to be non-subclass "
+ + defaultImplementationClazz);
+ }
+ defaultImplementation = ReflectionUtilities.getFullName(defaultImplementationClazz);
+ }
+ } else {
+ defaultImplementation = null;
+ }
+
+ return new ClassNodeImpl<T>(parent, simpleName, fullName, unit, injectable,
+ ExternalConstructor.class.isAssignableFrom(clazz),
+ injectableConstructors.toArray(new ConstructorDefImpl[0]),
+ allConstructors.toArray(new ConstructorDefImpl[0]), defaultImplementation);
+ }
+
+ /**
+ * XXX: This method assumes that all generic types have exactly one type parameter.
+ */
+ public static <T> NamedParameterNode<T> createNamedParameterNode(Node parent,
+ Class<? extends Name<T>> clazz, Type argClass) throws ClassHierarchyException {
+
+ Class<?> argRawClass = ReflectionUtilities.getRawClass(argClass);
+
+ final boolean isSet = argRawClass.equals(Set.class);
+ final boolean isList = argRawClass.equals(List.class);
+
+
+ if (isSet || isList) {
+ argClass = ReflectionUtilities.getInterfaceTarget(Collection.class, argClass);
+ argRawClass = ReflectionUtilities.getRawClass(argClass);
+ }
+
+ final String simpleName = ReflectionUtilities.getSimpleName(clazz);
+ final String fullName = ReflectionUtilities.getFullName(clazz);
+ final String fullArgName = ReflectionUtilities.getFullName(argClass);
+ final String simpleArgName = ReflectionUtilities.getSimpleName(argClass);
+
+
+ final NamedParameter namedParameter = clazz.getAnnotation(NamedParameter.class);
+
+ if (namedParameter == null) {
+ throw new IllegalStateException("Got name without named parameter post-validation!");
+ }
+ final boolean hasStringDefault, hasClassDefault, hasStringSetDefault, hasClassSetDefault;
+
+ int default_count = 0;
+ if (!namedParameter.default_value().isEmpty()) {
+ hasStringDefault = true;
+ default_count++;
+ } else {
+ hasStringDefault = false;
+ }
+ if (namedParameter.default_class() != Void.class) {
+ hasClassDefault = true;
+ default_count++;
+ } else {
+ hasClassDefault = false;
+ }
+ if (namedParameter.default_values() != null && namedParameter.default_values().length > 0) {
+ hasStringSetDefault = true;
+ default_count++;
+ } else {
+ hasStringSetDefault = false;
+ }
+ if (namedParameter.default_classes() != null && namedParameter.default_classes().length > 0) {
+ hasClassSetDefault = true;
+ default_count++;
+ } else {
+ hasClassSetDefault = false;
+ }
+ if (default_count > 1) {
+ throw new ClassHierarchyException("Named parameter " + fullName + " defines more than one of default_value, default_class, default_values and default_classes");
+ }
+
+ final String[] defaultInstanceAsStrings;
+
+ if (default_count == 0) {
+ defaultInstanceAsStrings = new String[]{};
+ } else if (hasClassDefault) {
+ final Class<?> default_class = namedParameter.default_class();
+ assertIsSubclassOf(clazz, default_class, argClass);
+ defaultInstanceAsStrings = new String[]{ReflectionUtilities.getFullName(default_class)};
+ } else if (hasStringDefault) {
+ // Don't know if the string is a class or literal here, so don't bother validating.
+ defaultInstanceAsStrings = new String[]{namedParameter.default_value()};
+ } else if (hasClassSetDefault) {
+ Class<?>[] clzs = namedParameter.default_classes();
+ defaultInstanceAsStrings = new String[clzs.length];
+ for (int i = 0; i < clzs.length; i++) {
+ assertIsSubclassOf(clazz, clzs[i], argClass);
+ defaultInstanceAsStrings[i] = ReflectionUtilities.getFullName(clzs[i]);
+ }
+ } else if (hasStringSetDefault) {
+ defaultInstanceAsStrings = namedParameter.default_values();
+ } else {
+ throw new IllegalStateException();
+ }
+
+ final String documentation = namedParameter.doc();
+
+ final String shortName = namedParameter.short_name().isEmpty()
+ ? null : namedParameter.short_name();
+
+ return new NamedParameterNodeImpl<>(parent, simpleName, fullName,
+ fullArgName, simpleArgName, isSet, isList, documentation, shortName, defaultInstanceAsStrings);
+ }
+
+ private static void assertIsSubclassOf(Class<?> named_parameter, Class<?> default_class,
+ Type argClass) {
+ boolean isSubclass = false;
+ boolean isGenericSubclass = false;
+ Class<?> argRawClass = ReflectionUtilities.getRawClass(argClass);
+
+ // Note: We intentionally strip the raw type information here. The reason is to handle
+ // EventHandler-style patterns and collections.
+
+ /// If we have a Name that takes EventHandler<A>, we want to be able to pass in an EventHandler<Object>.
+
+ for (final Type c : ReflectionUtilities.classAndAncestors(default_class)) {
+ if (ReflectionUtilities.getRawClass(c).equals(argRawClass)) {
+ isSubclass = true;
+ if (argClass instanceof ParameterizedType &&
+ c instanceof ParameterizedType) {
+ ParameterizedType argPt = (ParameterizedType) argClass;
+ ParameterizedType defaultPt = (ParameterizedType) c;
+
+ Class<?> rawDefaultParameter = ReflectionUtilities.getRawClass(defaultPt.getActualTypeArguments()[0]);
+ Class<?> rawArgParameter = ReflectionUtilities.getRawClass(argPt.getActualTypeArguments()[0]);
+
+ for (final Type d : ReflectionUtilities.classAndAncestors(argPt.getActualTypeArguments()[0])) {
+ if (ReflectionUtilities.getRawClass(d).equals(rawDefaultParameter)) {
+ isGenericSubclass = true;
+ }
+ }
+ for (final Type d : ReflectionUtilities.classAndAncestors(defaultPt.getActualTypeArguments()[0])) {
+ if (ReflectionUtilities.getRawClass(d).equals(rawArgParameter)) {
+ isGenericSubclass = true;
+ }
+ }
+ } else {
+ isGenericSubclass = true;
+ }
+ }
+ }
+
+ if (!(isSubclass)) {
+ throw new ClassHierarchyException(named_parameter + " defines a default class "
+ + ReflectionUtilities.getFullName(default_class) + " with a raw type that does not extend of its target's raw type " + argRawClass);
+ }
+ if (!(isGenericSubclass)) {
+ throw new ClassHierarchyException(named_parameter + " defines a default class "
+ + ReflectionUtilities.getFullName(default_class) + " with a type that does not extend its target's type " + argClass);
+ }
+ }
+
+ public static PackageNode createRootPackageNode() {
+ return new PackageNodeImpl();
+ }
+
+ private static <T> ConstructorDef<T> createConstructorDef(
+ boolean isClassInjectionCandidate, Constructor<T> constructor,
+ boolean injectable) throws ClassHierarchyException {
+ // We don't support injection of non-static member classes with @Inject
+ // annotations.
+ if (injectable && !isClassInjectionCandidate) {
+ throw new ClassHierarchyException("Cannot @Inject non-static member class unless the enclosing class an @Unit. Nested class is:"
+ + ReflectionUtilities.getFullName(constructor.getDeclaringClass()));
+ }
+ // TODO: When we use paramTypes below, we strip generic parameters. Is that OK?
+ Class<?>[] paramTypes = constructor.getParameterTypes();
+ Type[] genericParamTypes = constructor.getGenericParameterTypes();
+ Annotation[][] paramAnnotations = constructor.getParameterAnnotations();
+ if (paramTypes.length != paramAnnotations.length) {
+ throw new IllegalStateException();
+ }
+ ConstructorArg[] args = new ConstructorArg[genericParamTypes.length];
+ for (int i = 0; i < genericParamTypes.length; i++) {
+ // If this parameter is an injection future, unwrap the target class,
+ // and remember by setting isFuture to true.
+ final Type type;
+ final boolean isFuture;
+ if (InjectionFuture.class.isAssignableFrom(paramTypes[i])) {
+ type = ReflectionUtilities.getInterfaceTarget(InjectionFuture.class, genericParamTypes[i]);
+ isFuture = true;
+ } else {
+ type = paramTypes[i];
+ isFuture = false;
+ }
+ // Make node of the named parameter annotation (if any).
+ Parameter named = null;
+ for (int j = 0; j < paramAnnotations[i].length; j++) {
+ Annotation annotation = paramAnnotations[i][j];
+ if (annotation instanceof Parameter) {
+ if ((!isClassInjectionCandidate) || !injectable) {
+ throw new ClassHierarchyException(constructor + " is not injectable, but it has an @Parameter annotation.");
+ }
+ named = (Parameter) annotation;
+ }
+ }
+ args[i] = new ConstructorArgImpl(
+ ReflectionUtilities.getFullName(type), named == null ? null
+ : ReflectionUtilities.getFullName(named.value()),
+ isFuture);
+ }
+ return new ConstructorDefImpl<T>(
+ ReflectionUtilities.getFullName(constructor.getDeclaringClass()),
+ args, injectable);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/package-info.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/package-info.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/package-info.java
new file mode 100644
index 0000000..cc8cbc0
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/package-info.java
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/**
+ * Private implementation classes that configure and inject code written
+ * in Java. Tang supports cross-language object injections. The classes
+ * in org.apache.reef.tang.implementation are agnostic to the application
+ * language. In contrast, the classes in this package provide convenience
+ * APIs that move type-checking to Java's generic type system, and are also
+ * responsible for actually injecting objects (since, by definition, Java
+ * JVMs inject Java objects).
+ */
+package org.apache.reef.tang.implementation.java;
+
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/package-info.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/package-info.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/package-info.java
new file mode 100644
index 0000000..7c68e79
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/package-info.java
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/**
+ * Tang's implementation. Ideally, applications should never need to be
+ * aware of anything in this package or its sub-packages. As of this
+ * writing, some layering violations still exist, and portions of this
+ * package (such as the InjectionPlan APIs) are needed in rare
+ * circumstances.
+ */
+package org.apache.reef.tang.implementation;
+
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/protobuf/ProtocolBufferClassHierarchy.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/protobuf/ProtocolBufferClassHierarchy.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/protobuf/ProtocolBufferClassHierarchy.java
new file mode 100644
index 0000000..8053078
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/protobuf/ProtocolBufferClassHierarchy.java
@@ -0,0 +1,396 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.reef.tang.implementation.protobuf;
+
+import org.apache.reef.tang.ClassHierarchy;
+import org.apache.reef.tang.exceptions.NameResolutionException;
+import org.apache.reef.tang.implementation.types.*;
+import org.apache.reef.tang.proto.ClassHierarchyProto;
+import org.apache.reef.tang.types.*;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+public class ProtocolBufferClassHierarchy implements ClassHierarchy {
+
+ private static final String regex = "[\\.\\$\\+]";
+ private final PackageNode namespace;
+ private HashMap<String, Node> lookupTable = new HashMap<>();
+
+ // ############## Serialize implementation ##############
+
+ // protoc doesn't believe in auto-generating constructors, so here are
+ // hand-generated ones. *sigh*
+
+ /**
+ * Deserialize a class hierarchy from a protocol buffer object. The resulting
+ * object is immutable, and does not make use of reflection to fill in any
+ * missing values. This allows it to represent non-native classes as well
+ * as snapshots of Java class hierarchies.
+ */
+ public ProtocolBufferClassHierarchy(ClassHierarchyProto.Node root) {
+ namespace = new PackageNodeImpl();
+ if (!root.hasPackageNode()) {
+ throw new IllegalArgumentException("Expected a package node. Got: "
+ + root);
+ }
+ // Register all the classes.
+ for (ClassHierarchyProto.Node child : root.getChildrenList()) {
+ parseSubHierarchy(namespace, child);
+ }
+ buildLookupTable(namespace);
+ // Now, register the implementations
+ for (ClassHierarchyProto.Node child : root.getChildrenList()) {
+ wireUpInheritanceRelationships(child);
+ }
+ }
+
+ private static ClassHierarchyProto.Node newClassNode(String name,
+ String fullName, boolean isInjectionCandidate,
+ boolean isExternalConstructor, boolean isUnit,
+ List<ClassHierarchyProto.ConstructorDef> injectableConstructors,
+ List<ClassHierarchyProto.ConstructorDef> otherConstructors,
+ List<String> implFullNames, Iterable<ClassHierarchyProto.Node> children) {
+ return ClassHierarchyProto.Node
+ .newBuilder()
+ .setName(name)
+ .setFullName(fullName)
+ .setClassNode(
+ ClassHierarchyProto.ClassNode.newBuilder()
+ .setIsInjectionCandidate(isInjectionCandidate)
+ .setIsExternalConstructor(isExternalConstructor)
+ .setIsUnit(isUnit)
+ .addAllInjectableConstructors(injectableConstructors)
+ .addAllOtherConstructors(otherConstructors)
+ .addAllImplFullNames(implFullNames).build())
+ .addAllChildren(children).build();
+ }
+
+ private static ClassHierarchyProto.Node newNamedParameterNode(String name,
+ String fullName, String simpleArgClassName, String fullArgClassName,
+ boolean isSet,
+ boolean isList,
+ String documentation, // can be null
+ String shortName, // can be null
+ String[] instanceDefault, // can be null
+ Iterable<ClassHierarchyProto.Node> children) {
+ ClassHierarchyProto.NamedParameterNode.Builder namedParameterNodeBuilder
+ = ClassHierarchyProto.NamedParameterNode.newBuilder()
+ .setSimpleArgClassName(simpleArgClassName)
+ .setFullArgClassName(fullArgClassName)
+ .setIsSet(isSet)
+ .setIsList(isList);
+ if (documentation != null) {
+ namedParameterNodeBuilder.setDocumentation(documentation);
+ }
+ if (shortName != null) {
+ namedParameterNodeBuilder.setShortName(shortName);
+ }
+ if (instanceDefault != null) {
+ namedParameterNodeBuilder.addAllInstanceDefault(Arrays.asList(instanceDefault));
+ }
+
+ return ClassHierarchyProto.Node.newBuilder().setName(name)
+ .setFullName(fullName)
+ .setNamedParameterNode(namedParameterNodeBuilder.build())
+ .addAllChildren(children).build();
+ }
+
+ private static ClassHierarchyProto.Node newPackageNode(String name,
+ String fullName, Iterable<ClassHierarchyProto.Node> children) {
+ return ClassHierarchyProto.Node.newBuilder()
+ .setPackageNode(ClassHierarchyProto.PackageNode.newBuilder().build())
+ .setName(name).setFullName(fullName).addAllChildren(children).build();
+ }
+
+ private static ClassHierarchyProto.ConstructorDef newConstructorDef(
+ String fullClassName, List<ClassHierarchyProto.ConstructorArg> args) {
+ return ClassHierarchyProto.ConstructorDef.newBuilder()
+ .setFullClassName(fullClassName).addAllArgs(args).build();
+ }
+
+ // these serialize...() methods copy a pieces of the class hierarchy into
+ // protobufs
+
+ private static ClassHierarchyProto.ConstructorArg newConstructorArg(
+ String fullArgClassName, String namedParameterName, boolean isFuture) {
+ ClassHierarchyProto.ConstructorArg.Builder builder =
+ ClassHierarchyProto.ConstructorArg.newBuilder()
+ .setFullArgClassName(fullArgClassName)
+ .setIsInjectionFuture(isFuture);
+ if (namedParameterName != null) {
+ builder.setNamedParameterName(namedParameterName).build();
+ }
+ return builder.build();
+ }
+
+ private static ClassHierarchyProto.ConstructorDef serializeConstructorDef(
+ ConstructorDef<?> def) {
+ List<ClassHierarchyProto.ConstructorArg> args = new ArrayList<>();
+ for (ConstructorArg arg : def.getArgs()) {
+ args.add(newConstructorArg(arg.getType(), arg.getNamedParameterName(), arg.isInjectionFuture()));
+ }
+ return newConstructorDef(def.getClassName(), args);
+ }
+
+ private static ClassHierarchyProto.Node serializeNode(Node n) {
+ List<ClassHierarchyProto.Node> children = new ArrayList<>();
+ for (Node child : n.getChildren()) {
+ children.add(serializeNode(child));
+ }
+ if (n instanceof ClassNode) {
+ ClassNode<?> cn = (ClassNode<?>) n;
+ ConstructorDef<?>[] injectable = cn.getInjectableConstructors();
+ ConstructorDef<?>[] all = cn.getAllConstructors();
+ List<ConstructorDef<?>> others = new ArrayList<>(Arrays.asList(all));
+ others.removeAll(Arrays.asList(injectable));
+
+ List<ClassHierarchyProto.ConstructorDef> injectableConstructors = new ArrayList<>();
+ for (ConstructorDef<?> inj : injectable) {
+ injectableConstructors.add(serializeConstructorDef(inj));
+ }
+ List<ClassHierarchyProto.ConstructorDef> otherConstructors = new ArrayList<>();
+ for (ConstructorDef<?> other : others) {
+ otherConstructors.add(serializeConstructorDef(other));
+ }
+ List<String> implFullNames = new ArrayList<>();
+ for (ClassNode<?> impl : cn.getKnownImplementations()) {
+ implFullNames.add(impl.getFullName());
+ }
+ return newClassNode(cn.getName(), cn.getFullName(),
+ cn.isInjectionCandidate(), cn.isExternalConstructor(), cn.isUnit(),
+ injectableConstructors, otherConstructors, implFullNames, children);
+ } else if (n instanceof NamedParameterNode) {
+ NamedParameterNode<?> np = (NamedParameterNode<?>) n;
+ return newNamedParameterNode(np.getName(), np.getFullName(),
+ np.getSimpleArgName(), np.getFullArgName(), np.isSet(), np.isList(), np.getDocumentation(),
+ np.getShortName(), np.getDefaultInstanceAsStrings(), children);
+ } else if (n instanceof PackageNode) {
+ return newPackageNode(n.getName(), n.getFullName(), children);
+ } else {
+ throw new IllegalStateException("Encountered unknown type of Node: " + n);
+ }
+ }
+
+ /**
+ * Serialize a class hierarchy into a protocol buffer object.
+ *
+ * @param classHierarchy
+ * @return
+ */
+ public static ClassHierarchyProto.Node serialize(ClassHierarchy classHierarchy) {
+ return serializeNode(classHierarchy.getNamespace());
+ }
+
+ /**
+ * serialize a class hierarchy into a file
+ *
+ * @param file
+ * @param classHierarchy
+ * @throws IOException
+ */
+ public static void serialize(final File file, final ClassHierarchy classHierarchy) throws IOException {
+ final ClassHierarchyProto.Node node = serializeNode(classHierarchy.getNamespace());
+ try (final FileOutputStream output = new FileOutputStream(file)) {
+ try (final DataOutputStream dos = new DataOutputStream(output)) {
+ node.writeTo(dos);
+ }
+ }
+ }
+
+ /**
+ * Deserialize a class hierarchy from a file. The file can be generated from either Java or C#
+ *
+ * @param file
+ * @return
+ * @throws IOException
+ */
+ public static ClassHierarchy deserialize(final File file) throws IOException {
+ try (final InputStream stream = new FileInputStream(file)) {
+ final ClassHierarchyProto.Node root = ClassHierarchyProto.Node.parseFrom(stream);
+ return new ProtocolBufferClassHierarchy(root);
+ }
+ }
+
+ private static void parseSubHierarchy(Node parent, ClassHierarchyProto.Node n) {
+ final Node parsed;
+ if (n.hasPackageNode()) {
+ parsed = new PackageNodeImpl(parent, n.getName(), n.getFullName());
+ } else if (n.hasNamedParameterNode()) {
+ ClassHierarchyProto.NamedParameterNode np = n.getNamedParameterNode();
+ parsed = new NamedParameterNodeImpl<Object>(parent, n.getName(),
+ n.getFullName(), np.getFullArgClassName(), np.getSimpleArgClassName(),
+ np.getIsSet(), np.getIsList(), np.getDocumentation(), np.getShortName(),
+ np.getInstanceDefaultList().toArray(new String[0]));
+ } else if (n.hasClassNode()) {
+ ClassHierarchyProto.ClassNode cn = n.getClassNode();
+ List<ConstructorDef<?>> injectableConstructors = new ArrayList<>();
+ List<ConstructorDef<?>> allConstructors = new ArrayList<>();
+
+ for (ClassHierarchyProto.ConstructorDef injectable : cn
+ .getInjectableConstructorsList()) {
+ ConstructorDef<?> def = parseConstructorDef(injectable, true);
+ injectableConstructors.add(def);
+ allConstructors.add(def);
+ }
+ for (ClassHierarchyProto.ConstructorDef other : cn
+ .getOtherConstructorsList()) {
+ ConstructorDef<?> def = parseConstructorDef(other, false);
+ allConstructors.add(def);
+
+ }
+ @SuppressWarnings("unchecked")
+ ConstructorDef<Object>[] dummy = new ConstructorDef[0];
+ parsed = new ClassNodeImpl<>(parent, n.getName(), n.getFullName(),
+ cn.getIsUnit(), cn.getIsInjectionCandidate(),
+ cn.getIsExternalConstructor(), injectableConstructors.toArray(dummy),
+ allConstructors.toArray(dummy), cn.getDefaultImplementation());
+ } else {
+ throw new IllegalStateException("Bad protocol buffer: got abstract node"
+ + n);
+ }
+ for (ClassHierarchyProto.Node child : n.getChildrenList()) {
+ parseSubHierarchy(parsed, child);
+ }
+ }
+
+ private static ConstructorDef<?> parseConstructorDef(
+ org.apache.reef.tang.proto.ClassHierarchyProto.ConstructorDef def,
+ boolean isInjectable) {
+ List<ConstructorArg> args = new ArrayList<>();
+ for (ClassHierarchyProto.ConstructorArg arg : def.getArgsList()) {
+ args.add(new ConstructorArgImpl(arg.getFullArgClassName(), arg
+ .getNamedParameterName(), arg.getIsInjectionFuture()));
+ }
+ return new ConstructorDefImpl<>(def.getFullClassName(),
+ args.toArray(new ConstructorArg[0]), isInjectable);
+ }
+
+ private static String getNthPrefix(String str, int n) {
+ n++; // want this function to be zero indexed...
+ for (int i = 0; i < str.length(); i++) {
+ char c = str.charAt(i);
+ if (c == '.' || c == '$' || c == '+') {
+ n--;
+ }
+ if (n == 0) {
+ return str.substring(0, i);
+ }
+ }
+ if (n == 1) {
+ return str;
+ } else {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ }
+
+ private void buildLookupTable(Node n) {
+ for (Node child : n.getChildren()) {
+ lookupTable.put(child.getFullName(), child);
+ buildLookupTable(child);
+ }
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private void wireUpInheritanceRelationships(final ClassHierarchyProto.Node n) {
+ if (n.hasClassNode()) {
+ final ClassHierarchyProto.ClassNode cn = n.getClassNode();
+ final ClassNode iface;
+ try {
+ iface = (ClassNode) getNode(n.getFullName());
+ } catch (NameResolutionException e) {
+ throw new IllegalStateException("When reading protocol buffer node "
+ + n.getFullName() + " does not exist. Full record is " + n, e);
+ }
+ for (String impl : cn.getImplFullNamesList()) {
+ try {
+ iface.putImpl((ClassNode) getNode(impl));
+ } catch (NameResolutionException e) {
+ throw new IllegalStateException("When reading protocol buffer node "
+ + n + " refers to non-existent implementation:" + impl);
+ } catch (ClassCastException e) {
+ try {
+ throw new IllegalStateException(
+ "When reading protocol buffer node " + n
+ + " found implementation" + getNode(impl)
+ + " which is not a ClassNode!");
+ } catch (NameResolutionException e2) {
+ throw new IllegalStateException(
+ "Got 'cant happen' exception when producing error message for "
+ + e);
+ }
+ }
+ }
+ }
+
+ for (ClassHierarchyProto.Node child : n.getChildrenList()) {
+ wireUpInheritanceRelationships(child);
+ }
+ }
+
+ @Override
+ public Node getNode(String fullName) throws NameResolutionException {
+
+ Node ret = lookupTable.get(fullName);
+/* String[] tok = fullName.split(regex);
+
+ Node ret = namespace.get(fullName);
+ for (int i = 0; i < tok.length; i++) {
+ Node n = namespace.get(getNthPrefix(fullName, i));
+ if (n != null) {
+ for (i++; i < tok.length; i++) {
+ n = n.get(tok[i]);
+ if (n == null) {
+ throw new NameResolutionException(fullName, getNthPrefix(fullName,
+ i - 1));
+ }
+ }
+ return n;
+ }
+ } */
+ if (ret != null) {
+ return ret;
+ } else {
+ throw new NameResolutionException(fullName, "");
+ }
+ }
+
+ @Override
+ public boolean isImplementation(ClassNode<?> inter, ClassNode<?> impl) {
+ return impl.isImplementationOf(inter);
+ }
+
+ @Override
+ public ClassHierarchy merge(ClassHierarchy ch) {
+ if (this == ch) {
+ return this;
+ }
+ throw new UnsupportedOperationException(
+ "Cannot merge ExternalClassHierarchies yet!");
+ }
+
+ @Override
+ public Node getNamespace() {
+ return namespace;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/protobuf/ProtocolBufferInjectionPlan.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/protobuf/ProtocolBufferInjectionPlan.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/protobuf/ProtocolBufferInjectionPlan.java
new file mode 100644
index 0000000..9615d52
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/protobuf/ProtocolBufferInjectionPlan.java
@@ -0,0 +1,148 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.reef.tang.implementation.protobuf;
+
+import org.apache.reef.tang.ClassHierarchy;
+import org.apache.reef.tang.exceptions.BindException;
+import org.apache.reef.tang.exceptions.NameResolutionException;
+import org.apache.reef.tang.implementation.Constructor;
+import org.apache.reef.tang.implementation.InjectionPlan;
+import org.apache.reef.tang.implementation.Subplan;
+import org.apache.reef.tang.implementation.java.JavaInstance;
+import org.apache.reef.tang.proto.InjectionPlanProto;
+import org.apache.reef.tang.types.ClassNode;
+import org.apache.reef.tang.types.ConstructorDef;
+import org.apache.reef.tang.types.Node;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ProtocolBufferInjectionPlan {
+
+ <T> InjectionPlanProto.InjectionPlan newConstructor(final String fullName,
+ List<InjectionPlanProto.InjectionPlan> plans) {
+ return InjectionPlanProto.InjectionPlan
+ .newBuilder()
+ .setName(fullName)
+ .setConstructor(
+ InjectionPlanProto.Constructor.newBuilder().addAllArgs(plans)
+ .build()).build();
+ }
+
+ <T> InjectionPlanProto.InjectionPlan newSubplan(final String fullName,
+ int selectedPlan, List<InjectionPlanProto.InjectionPlan> plans) {
+ return InjectionPlanProto.InjectionPlan
+ .newBuilder()
+ .setName(fullName)
+ .setSubplan(
+ InjectionPlanProto.Subplan.newBuilder()
+ .setSelectedPlan(selectedPlan).addAllPlans(plans).build())
+ .build();
+ }
+
+ <T> InjectionPlanProto.InjectionPlan newInstance(final String fullName,
+ final String value) {
+ return InjectionPlanProto.InjectionPlan.newBuilder().setName(fullName)
+ .setInstance(InjectionPlanProto.Instance.newBuilder().setValue(value))
+ .build();
+ }
+
+ public <T> InjectionPlanProto.InjectionPlan serialize(InjectionPlan<T> ip) {
+ if (ip instanceof Constructor) {
+ Constructor<T> cons = (Constructor<T>) ip;
+ InjectionPlan<?>[] args = cons.getArgs();
+ InjectionPlanProto.InjectionPlan[] protoArgs = new InjectionPlanProto.InjectionPlan[args.length];
+ for (int i = 0; i < args.length; i++) {
+ protoArgs[i] = serialize(args[i]);
+ }
+ return newConstructor(ip.getNode().getFullName(),
+ Arrays.asList(protoArgs));
+ } else if (ip instanceof Subplan) {
+ Subplan<T> sp = (Subplan<T>) ip;
+ InjectionPlan<?>[] args = sp.getPlans();
+ InjectionPlanProto.InjectionPlan[] subPlans = new InjectionPlanProto.InjectionPlan[args.length];
+ for (int i = 0; i < args.length; i++) {
+ subPlans[i] = serialize(args[i]);
+ }
+ return newSubplan(ip.getNode().getFullName(), sp.getSelectedIndex(),
+ Arrays.asList(subPlans));
+ } else if (ip instanceof JavaInstance) {
+ JavaInstance<T> ji = (JavaInstance<T>) ip;
+ return newInstance(ip.getNode().getFullName(), ji.getInstanceAsString());
+ } else {
+ throw new IllegalStateException(
+ "Encountered unknown type of InjectionPlan: " + ip);
+ }
+ }
+
+ private Object parse(String type, String value) {
+ // XXX this is a placeholder for now. We need a parser API that will
+ // either produce a live java object or (partially) validate stuff to
+ // see if it looks like the target language will be able to handle this
+ // type + value.
+ return value;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> InjectionPlan<T> deserialize(ClassHierarchy ch,
+ InjectionPlanProto.InjectionPlan ip) throws NameResolutionException,
+ BindException {
+ final String fullName = ip.getName();
+ if (ip.hasConstructor()) {
+ final InjectionPlanProto.Constructor cons = ip.getConstructor();
+
+ final ClassNode<T> cn = (ClassNode<T>) ch.getNode(fullName);
+
+ final InjectionPlanProto.InjectionPlan protoBufArgs[] = cons
+ .getArgsList().toArray(new InjectionPlanProto.InjectionPlan[0]);
+ final ClassNode<?>[] cnArgs = new ClassNode[protoBufArgs.length];
+
+ for (int i = 0; i < protoBufArgs.length; i++) {
+ cnArgs[i] = (ClassNode<?>) ch.getNode(protoBufArgs[i].getName());
+ }
+
+ final InjectionPlan<?> ipArgs[] = new InjectionPlan[protoBufArgs.length];
+
+ for (int i = 0; i < protoBufArgs.length; i++) {
+ ipArgs[i] = (InjectionPlan<?>) deserialize(ch, protoBufArgs[i]);
+ }
+
+ final ConstructorDef<T> constructor = cn.getConstructorDef(cnArgs);
+ return new Constructor<T>(cn, constructor, ipArgs);
+ } else if (ip.hasInstance()) {
+ InjectionPlanProto.Instance ins = ip.getInstance();
+ T instance = (T) parse(ip.getName(), ins.getValue());
+ return new JavaInstance<T>(ch.getNode(ip.getName()), instance);
+ } else if (ip.hasSubplan()) {
+ final InjectionPlanProto.Subplan subplan = ip.getSubplan();
+ final InjectionPlanProto.InjectionPlan protoBufPlans[] = subplan
+ .getPlansList().toArray(new InjectionPlanProto.InjectionPlan[0]);
+
+ final InjectionPlan<T> subPlans[] = new InjectionPlan[protoBufPlans.length];
+ for (int i = 0; i < protoBufPlans.length; i++) {
+ subPlans[i] = (InjectionPlan<T>) deserialize(ch, protoBufPlans[i]);
+ }
+ Node n = ch.getNode(fullName);
+ return new Subplan<T>(n, subPlans);
+ } else {
+ throw new IllegalStateException(
+ "Encountered unknown type of injection plan: " + ip);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/protobuf/package-info.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/protobuf/package-info.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/protobuf/package-info.java
new file mode 100644
index 0000000..da901a2
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/protobuf/package-info.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/**
+ * Implementation classes that translate between Tang's core API and protocol
+ * buffers. This enables cross-language injection sessions.
+ */
+
+package org.apache.reef.tang.implementation.protobuf;
+