This project has retired. For details please refer to its Attic page.
Source code
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.formats;
020
021import org.apache.reef.tang.ExternalConstructor;
022import org.apache.reef.tang.JavaConfigurationBuilder;
023import org.apache.reef.tang.Tang;
024import org.apache.reef.tang.annotations.Name;
025import org.apache.reef.tang.exceptions.BindException;
026import org.apache.reef.tang.exceptions.ClassHierarchyException;
027import org.apache.reef.tang.exceptions.NameResolutionException;
028import org.apache.reef.tang.util.MonotonicHashMap;
029import org.apache.reef.tang.util.MonotonicHashSet;
030import org.apache.reef.tang.util.ReflectionUtilities;
031
032import java.lang.reflect.Field;
033import java.lang.reflect.Modifier;
034import java.util.List;
035import java.util.Map;
036import java.util.Set;
037
038public abstract class ConfigurationModuleBuilder {
039
040  private final static Set<Class<?>> paramBlacklist = new MonotonicHashSet<Class<?>>(
041      Param.class, Impl.class);
042  private final static Set<Class<?>> paramTypes = new MonotonicHashSet<Class<?>>(
043      RequiredImpl.class, OptionalImpl.class, RequiredParameter.class,
044      OptionalParameter.class);
045  final JavaConfigurationBuilder b = Tang.Factory.getTang()
046      .newConfigurationBuilder();
047  // Sets of things that have been declared
048  final Set<Field> reqDecl = new MonotonicHashSet<>();
049  final Set<Object> setOpts = new MonotonicHashSet<>();
050  // Maps from field instance variables to the fields that they
051  // are assigned to. These better be unique!
052  final Map<Object, Field> map = new MonotonicHashMap<>();
053  final Map<Class<?>, Impl<?>> freeImpls = new MonotonicHashMap<>();
054  final Map<Class<? extends Name<?>>, Param<?>> freeParams = new MonotonicHashMap<>();
055  private final Set<Field> optDecl = new MonotonicHashSet<>();
056  // Set of things that have been used in a bind. These must be equal
057  // to the decl counterparts before build() is called.
058  private final Set<Field> reqUsed = new MonotonicHashSet<>();
059  private final Set<Field> optUsed = new MonotonicHashSet<>();
060  private final Map<Class<?>, String> lateBindClazz = new MonotonicHashMap<>();
061
062  protected ConfigurationModuleBuilder() {
063    for (Field f : getClass().getDeclaredFields()) {
064      Class<?> t = f.getType();
065      if (paramBlacklist.contains(t)) {
066        throw new ClassHierarchyException(
067            "Found a field of type " + t + " which should be a Required/Optional Parameter/Implementation instead"
068        );
069      }
070      if (paramTypes.contains(t)) {
071        if (!Modifier.isPublic(f.getModifiers())) {
072          throw new ClassHierarchyException(
073              "Found a non-public configuration option in " + getClass() + ": "
074                  + f);
075        }
076        if (!Modifier.isStatic(f.getModifiers())) {
077          throw new ClassHierarchyException(
078              "Found a non-static configuration option in " + getClass() + ": "
079                  + f);
080        }
081        if (!Modifier.isFinal(f.getModifiers())) {
082          throw new ClassHierarchyException(
083              "Found a non-final configuration option in " + getClass() + ": "
084                  + f);
085        }
086        final Object o;
087        try {
088          o = f.get(null);
089        } catch (IllegalArgumentException | IllegalAccessException e) {
090          throw new ClassHierarchyException(
091              "Could not look up field instance in " + getClass() + " field: "
092                  + f, e);
093        }
094        if (map.containsKey(o)) {
095          throw new ClassHierarchyException(
096              "Detected aliased instances in class " + getClass()
097                  + " for fields " + map.get(o) + " and " + f);
098        }
099        if (t == RequiredImpl.class || t == RequiredParameter.class) {
100          reqDecl.add(f);
101        } else {
102          optDecl.add(f);
103        }
104        map.put(o, f);
105      }
106    }
107  }
108
109  private ConfigurationModuleBuilder(ConfigurationModuleBuilder c) {
110    try {
111      b.addConfiguration(c.b.build());
112    } catch (BindException e) {
113      throw new ClassHierarchyException(e);
114    }
115    reqDecl.addAll(c.reqDecl);
116    optDecl.addAll(c.optDecl);
117    reqUsed.addAll(c.reqUsed);
118    optUsed.addAll(c.optUsed);
119    setOpts.addAll(c.setOpts);
120    map.putAll(c.map);
121    freeImpls.putAll(c.freeImpls);
122    freeParams.putAll(c.freeParams);
123    lateBindClazz.putAll(c.lateBindClazz);
124
125  }
126
127  /**
128   * TODO: It would be nice if this incorporated d by reference so that static analysis / documentation tools
129   * could document the dependency between c and d.
130   */
131  public final ConfigurationModuleBuilder merge(ConfigurationModule d) {
132    if (d == null) {
133      throw new NullPointerException("If merge() was passed a static final field that is initialized to non-null, then this is almost certainly caused by a circular class dependency.");
134    }
135    try {
136      d.assertStaticClean();
137    } catch (ClassHierarchyException e) {
138      throw new ClassHierarchyException(ReflectionUtilities.getFullName(getClass()) + ": detected attempt to merge with ConfigurationModule that has had set() called on it", e);
139    }
140    ConfigurationModuleBuilder c = deepCopy();
141    try {
142      c.b.addConfiguration(d.builder.b.build());
143    } catch (BindException e) {
144      throw new ClassHierarchyException(e);
145    }
146    c.reqDecl.addAll(d.builder.reqDecl);
147    c.optDecl.addAll(d.builder.optDecl);
148    c.reqUsed.addAll(d.builder.reqUsed);
149    c.optUsed.addAll(d.builder.optUsed);
150    c.setOpts.addAll(d.builder.setOpts);
151    c.map.putAll(d.builder.map);
152    c.freeImpls.putAll(d.builder.freeImpls);
153    c.freeParams.putAll(d.builder.freeParams);
154    c.lateBindClazz.putAll(d.builder.lateBindClazz);
155
156    return c;
157  }
158
159  public final <T> ConfigurationModuleBuilder bind(Class<?> iface, Impl<?> opt) {
160    ConfigurationModuleBuilder c = deepCopy();
161    c.processUse(opt);
162    c.freeImpls.put(iface, opt);
163    return c;
164  }
165
166  public final <T> ConfigurationModuleBuilder bindSetEntry(Class<? extends Name<Set<T>>> iface, String impl) {
167    ConfigurationModuleBuilder c = deepCopy();
168    try {
169      c.b.bindSetEntry(iface, impl);
170    } catch (BindException e) {
171      throw new ClassHierarchyException(e);
172    }
173    return c;
174  }
175
176  public final <T> ConfigurationModuleBuilder bindSetEntry(Class<? extends Name<Set<T>>> iface, Class<? extends T> impl) {
177    ConfigurationModuleBuilder c = deepCopy();
178    try {
179      c.b.bindSetEntry(iface, impl);
180    } catch (BindException e) {
181      throw new ClassHierarchyException(e);
182    }
183    return c;
184  }
185
186  public final <T> ConfigurationModuleBuilder bindSetEntry(Class<? extends Name<Set<T>>> iface, Impl<? extends T> opt) {
187    ConfigurationModuleBuilder c = deepCopy();
188    c.processUse(opt);
189    c.freeImpls.put(iface, opt);
190    if (!setOpts.contains(opt)) {
191      c.setOpts.add(opt);
192    }
193    return c;
194  }
195
196  public final <T> ConfigurationModuleBuilder bindSetEntry(Class<? extends Name<Set<T>>> iface, Param<? extends T> opt) {
197    ConfigurationModuleBuilder c = deepCopy();
198    c.processUse(opt);
199    c.freeParams.put(iface, opt);
200    if (!setOpts.contains(opt)) {
201      c.setOpts.add(opt);
202    }
203    return c;
204  }
205
206
207  public final <T> ConfigurationModuleBuilder bindImplementation(Class<T> iface,
208                                                                 Class<? extends T> impl) {
209    ConfigurationModuleBuilder c = deepCopy();
210    try {
211      c.b.bindImplementation(iface, impl);
212    } catch (BindException e) {
213      throw new ClassHierarchyException(e);
214    }
215    return c;
216  }
217
218  public final <T> ConfigurationModuleBuilder bindImplementation(Class<T> iface,
219                                                                 String impl) {
220    ConfigurationModuleBuilder c = deepCopy();
221    c.lateBindClazz.put(iface, impl);
222    return c;
223  }
224
225  public final <T> ConfigurationModuleBuilder bindImplementation(Class<T> iface,
226                                                                 Impl<? extends T> opt) {
227    ConfigurationModuleBuilder c = deepCopy();
228    c.processUse(opt);
229    c.freeImpls.put(iface, opt);
230    return c;
231  }
232
233  public final <T> ConfigurationModuleBuilder bindNamedParameter(
234      Class<? extends Name<T>> name, String value) {
235    ConfigurationModuleBuilder c = deepCopy();
236    try {
237      c.b.bindNamedParameter(name, value);
238    } catch (BindException e) {
239      throw new ClassHierarchyException(e);
240    }
241    return c;
242  }
243
244  public final <T> ConfigurationModuleBuilder bindNamedParameter(
245      Class<? extends Name<T>> name, Param<T> opt) {
246    ConfigurationModuleBuilder c = deepCopy();
247    c.processUse(opt);
248    c.freeParams.put(name, opt);
249    return c;
250  }
251
252  public final <T> ConfigurationModuleBuilder bindNamedParameter(
253      Class<? extends Name<T>> iface, Class<? extends T> impl) {
254    ConfigurationModuleBuilder c = deepCopy();
255    try {
256      c.b.bindNamedParameter(iface, impl);
257    } catch (BindException e) {
258      throw new ClassHierarchyException(e);
259    }
260    return c;
261  }
262
263  public final <T> ConfigurationModuleBuilder bindNamedParameter(
264      Class<? extends Name<T>> iface, Impl<? extends T> opt) {
265    ConfigurationModuleBuilder c = deepCopy();
266    c.processUse(opt);
267    c.freeImpls.put(iface, opt);
268    return c;
269  }
270
271  public final <T> ConfigurationModuleBuilder bindConstructor(Class<T> clazz,
272                                                              Class<? extends ExternalConstructor<? extends T>> constructor) {
273    ConfigurationModuleBuilder c = deepCopy();
274    try {
275      c.b.bindConstructor(clazz, constructor);
276    } catch (BindException e) {
277      throw new ClassHierarchyException(e);
278    }
279    return c;
280  }
281
282  public final <T> ConfigurationModuleBuilder bindConstructor(Class<T> cons,
283                                                              Impl<? extends ExternalConstructor<? extends T>> v) {
284    ConfigurationModuleBuilder c = deepCopy();
285    c.processUse(v);
286    c.freeImpls.put(cons, v);
287    return c;
288  }
289
290  public final <T> ConfigurationModuleBuilder bindList(Class<? extends Name<List<T>>> iface,
291                                                       Impl<List> opt) {
292    ConfigurationModuleBuilder c = deepCopy();
293    c.processUse(opt);
294    c.freeImpls.put(iface, opt);
295    return c;
296  }
297
298  public final <T> ConfigurationModuleBuilder bindList(Class<? extends Name<List<T>>> iface,
299                                                       Param<List> opt) {
300    ConfigurationModuleBuilder c = deepCopy();
301    c.processUse(opt);
302    c.freeParams.put(iface, opt);
303    return c;
304  }
305
306  public final <T> ConfigurationModuleBuilder bindList(Class<? extends Name<List<T>>> iface, List list) {
307    ConfigurationModuleBuilder c = deepCopy();
308    c.b.bindList(iface, list);
309    return c;
310  }
311
312  private final <T> void processUse(Object impl) {
313    Field f = map.get(impl);
314    if (f == null) { /* throw */
315      throw new ClassHierarchyException("Unknown Impl/Param when binding " + ReflectionUtilities.getSimpleName(impl.getClass()) + ".  Did you pass in a field from some other module?");
316    }
317    if (!reqUsed.contains(f)) {
318      reqUsed.add(f);
319    }
320    if (!optUsed.contains(f)) {
321      optUsed.add(f);
322    }
323  }
324
325  public final ConfigurationModule build() throws ClassHierarchyException {
326    ConfigurationModuleBuilder c = deepCopy();
327
328    if (!(c.reqUsed.containsAll(c.reqDecl) && c.optUsed.containsAll(c.optDecl))) {
329      Set<Field> fset = new MonotonicHashSet<>();
330      for (Field f : c.reqDecl) {
331        if (!c.reqUsed.contains(f)) {
332          fset.add(f);
333        }
334      }
335      for (Field f : c.optDecl) {
336        if (!c.optUsed.contains(f)) {
337          fset.add(f);
338        }
339      }
340      throw new ClassHierarchyException(
341          "Found declared options that were not used in binds: "
342              + toString(fset));
343    }
344    for (Class<?> clz : c.lateBindClazz.keySet()) {
345      try {
346        c.b.bind(ReflectionUtilities.getFullName(clz), c.lateBindClazz.get(clz));
347      } catch (NameResolutionException e) {
348        throw new ClassHierarchyException("ConfigurationModule refers to unknown class: " + c.lateBindClazz.get(clz), e);
349      } catch (BindException e) {
350        throw new ClassHierarchyException("bind failed while initializing ConfigurationModuleBuilder", e);
351      }
352    }
353    return new ConfigurationModule(c);
354  }
355
356/*  public final <T> ConfigurationModuleBuilder bind(Class<T> iface, Class<?> impl) {
357    ConfigurationModuleBuilder c = deepCopy();
358    try {
359      c.b.bind(iface, impl);
360    } catch (BindException e) {
361      throw new ClassHierarchyException(e);
362    }
363    return c;
364  } */
365
366  final ConfigurationModuleBuilder deepCopy() {
367    // ooh... this is a dirty trick --- we strip this's type off here,
368    // fortunately, we've all ready looked at the root object's class's
369    // fields, and we copy the information we extracted from them, so
370    // everything works out OK w.r.t. field detection.
371    return new ConfigurationModuleBuilder(this) {
372    };
373  }
374
375  final String toString(Set<Field> s) {
376    StringBuilder sb = new StringBuilder("{");
377    boolean first = true;
378    for (Field f : s) {
379      sb.append((first ? " " : ", ") + f.getName());
380      first = false;
381    }
382    sb.append(" }");
383    return sb.toString();
384  }
385}