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.Configuration;
022import org.apache.reef.tang.annotations.Name;
023import org.apache.reef.tang.exceptions.BindException;
024import org.apache.reef.tang.exceptions.ClassHierarchyException;
025import org.apache.reef.tang.exceptions.NameResolutionException;
026import org.apache.reef.tang.types.NamedParameterNode;
027import org.apache.reef.tang.util.*;
028
029import java.lang.reflect.Field;
030import java.util.ArrayList;
031import java.util.List;
032import java.util.Map;
033import java.util.Map.Entry;
034import java.util.Set;
035
036/**
037 * Allows applications to bundle sets of configuration options together into
038 * discrete packages.  Unlike more conventional approaches,
039 * ConfigurationModules store such information in static data structures that
040 * can be statically discovered and sanity-checked.
041 *
042 * @see org.apache.reef.tang.formats.TestConfigurationModule for more information and examples.
043 */
044public class ConfigurationModule {
045  final ConfigurationModuleBuilder builder;
046  // Set of required unset parameters. Must be empty before build.
047  private final Set<Field> reqSet = new MonotonicHashSet<>();
048  private final Map<Impl<?>, Class<?>> setImpls = new MonotonicHashMap<>();
049  private final MonotonicMultiHashMap<Impl<?>, Class<?>> setImplSets = new MonotonicMultiHashMap<>();
050  private final MonotonicMultiHashMap<Impl<?>, String> setLateImplSets = new MonotonicMultiHashMap<>();
051  private final MonotonicMultiHashMap<Param<?>, String> setParamSets = new MonotonicMultiHashMap<>();
052  private final Map<Impl<?>, String> setLateImpls = new MonotonicHashMap<>();
053  private final Map<Param<?>, String> setParams = new MonotonicHashMap<>();
054  private final Map<Impl<List>, List<?>> setImplLists = new MonotonicHashMap<>();
055  private final Map<Param<List>, List<?>> setParamLists = new MonotonicHashMap<>();
056
057  protected ConfigurationModule(ConfigurationModuleBuilder builder) {
058    this.builder = builder.deepCopy();
059  }
060
061  private ConfigurationModule deepCopy() {
062    ConfigurationModule cm = new ConfigurationModule(builder.deepCopy());
063    cm.setImpls.putAll(setImpls);
064    cm.setImplSets.addAll(setImplSets);
065    cm.setLateImplSets.addAll(setLateImplSets);
066    cm.setParamSets.addAll(setParamSets);
067    cm.setLateImpls.putAll(setLateImpls);
068    cm.setParams.putAll(setParams);
069    cm.reqSet.addAll(reqSet);
070    cm.setImplLists.putAll(setImplLists);
071    cm.setParamLists.putAll(setParamLists);
072    return cm;
073  }
074
075  private final <T> void processSet(Object impl) {
076    Field f = builder.map.get(impl);
077    if (f == null) { /* throw */
078      throw new ClassHierarchyException("Unknown Impl/Param when setting " + ReflectionUtilities.getSimpleName(impl.getClass()) + ".  Did you pass in a field from some other module?");
079    }
080    if (!reqSet.contains(f)) {
081      reqSet.add(f);
082    }
083  }
084
085  public final <T> ConfigurationModule set(Impl<T> opt, Class<? extends T> impl) {
086    ConfigurationModule c = deepCopy();
087    c.processSet(opt);
088    if (c.builder.setOpts.contains(opt)) {
089      c.setImplSets.put(opt, impl);
090    } else {
091      c.setImpls.put(opt, impl);
092    }
093    return c;
094  }
095
096  public final <T> ConfigurationModule set(Impl<T> opt, String impl) {
097    ConfigurationModule c = deepCopy();
098    c.processSet(opt);
099    if (c.builder.setOpts.contains(opt)) {
100      c.setLateImplSets.put(opt, impl);
101    } else {
102      c.setLateImpls.put(opt, impl);
103    }
104    return c;
105  }
106
107  /**
108   * Binds a list to a specific optional/required Impl using ConfigurationModule.
109   *
110   * @param opt      Target optional/required Impl
111   * @param implList List object to be injected
112   * @param <T>
113   * @return
114   */
115  public final <T> ConfigurationModule set(Impl<List> opt, List implList) {
116    ConfigurationModule c = deepCopy();
117    c.processSet(opt);
118    c.setImplLists.put(opt, implList);
119    return c;
120  }
121
122  public final <T> ConfigurationModule set(Param<T> opt, Class<? extends T> val) {
123    return set(opt, ReflectionUtilities.getFullName(val));
124  }
125
126  public final ConfigurationModule set(Param<Boolean> opt, boolean val) {
127    return set(opt, "" + val);
128  }
129
130  public final ConfigurationModule set(Param<? extends Number> opt, Number val) {
131    return set(opt, "" + val);
132  }
133
134  public final <T> ConfigurationModule set(Param<T> opt, String val) {
135    ConfigurationModule c = deepCopy();
136    c.processSet(opt);
137    if (c.builder.setOpts.contains(opt)) {
138      c.setParamSets.put(opt, val);
139    } else {
140      c.setParams.put(opt, val);
141    }
142    return c;
143  }
144
145  /**
146   * Binds a list to a specfici optional/required Param using ConfigurationModule.
147   *
148   * @param opt      target optional/required Param
149   * @param implList List object to be injected
150   * @param <T>
151   * @return
152   */
153  public final <T> ConfigurationModule set(Param<List> opt, List implList) {
154    ConfigurationModule c = deepCopy();
155    c.processSet(opt);
156    c.setParamLists.put(opt, implList);
157    return c;
158  }
159
160  @SuppressWarnings({"unchecked", "rawtypes"})
161  public Configuration build() throws BindException {
162    ConfigurationModule c = deepCopy();
163
164    if (!c.reqSet.containsAll(c.builder.reqDecl)) {
165      Set<Field> missingSet = new MonotonicHashSet<>();
166      for (Field f : c.builder.reqDecl) {
167        if (!c.reqSet.contains(f)) {
168          missingSet.add(f);
169        }
170      }
171      throw new BindException(
172          "Attempt to build configuration before setting required option(s): "
173              + builder.toString(missingSet));
174    }
175
176    for (Class<?> clazz : c.builder.freeImpls.keySet()) {
177      Impl<?> i = c.builder.freeImpls.get(clazz);
178      if (c.setImpls.containsKey(i)) {
179        c.builder.b.bind(clazz, c.setImpls.get(i));
180      } else if (c.setLateImpls.containsKey(i)) {
181        c.builder.b.bind(ReflectionUtilities.getFullName(clazz), c.setLateImpls.get(i));
182      } else if (c.setImplSets.containsKey(i) || c.setLateImplSets.containsKey(i)) {
183        for (Class<?> clz : c.setImplSets.getValuesForKey(i)) {
184          c.builder.b.bindSetEntry((Class) clazz, (Class) clz);
185        }
186        for (String s : c.setLateImplSets.getValuesForKey(i)) {
187          c.builder.b.bindSetEntry((Class) clazz, s);
188        }
189      } else if (c.setImplLists.containsKey(i)) {
190        c.builder.b.bindList((Class) clazz, c.setImplLists.get(i));
191      }
192    }
193    for (Class<? extends Name<?>> clazz : c.builder.freeParams.keySet()) {
194      Param<?> p = c.builder.freeParams.get(clazz);
195      String s = c.setParams.get(p);
196      boolean foundOne = false;
197      if (s != null) {
198        c.builder.b.bindNamedParameter(clazz, s);
199        foundOne = true;
200      }
201      // Find the bound list for the NamedParameter
202      List list = c.setParamLists.get(p);
203      if (list != null) {
204        c.builder.b.bindList((Class) clazz, list);
205        foundOne = true;
206      }
207      for (String paramStr : c.setParamSets.getValuesForKey(p)) {
208        c.builder.b.bindSetEntry((Class) clazz, paramStr);
209        foundOne = true;
210      }
211      if (!foundOne) {
212        if (!(p instanceof OptionalParameter)) {
213          throw new IllegalStateException();
214        }
215      }
216    }
217    return c.builder.b.build();
218
219  }
220
221  public Set<NamedParameterNode<?>> getBoundNamedParameters() {
222    Configuration c = this.builder.b.build();
223    Set<NamedParameterNode<?>> nps = new MonotonicSet<>();
224    nps.addAll(c.getNamedParameters());
225    for (Class<?> np : this.builder.freeParams.keySet()) {
226      try {
227        nps.add((NamedParameterNode<?>) builder.b.getClassHierarchy().getNode(ReflectionUtilities.getFullName(np)));
228      } catch (NameResolutionException e) {
229        throw new IllegalStateException(e);
230      }
231    }
232    return nps;
233  }
234
235  public List<Entry<String, String>> toStringPairs() {
236    List<Entry<String, String>> ret = new ArrayList<>();
237    class MyEntry implements Entry<String, String> {
238      final String k;
239      final String v;
240
241      public MyEntry(String k, String v) {
242        this.k = k;
243        this.v = v;
244      }
245
246      @Override
247      public String getKey() {
248        return k;
249      }
250
251      @Override
252      public String getValue() {
253        return v;
254      }
255
256      @Override
257      public String setValue(String value) {
258        throw new UnsupportedOperationException();
259      }
260
261    }
262    for (Class<?> c : this.builder.freeParams.keySet()) {
263      ret.add(new MyEntry(ReflectionUtilities.getFullName(c), this.builder.map.get(this.builder.freeParams.get(c)).getName()));
264    }
265    for (Class<?> c : this.builder.freeImpls.keySet()) {
266      ret.add(new MyEntry(ReflectionUtilities.getFullName(c), this.builder.map.get(this.builder.freeImpls.get(c)).getName()));
267    }
268    for (String s : ConfigurationFile.toConfigurationStringList(builder.b.build())) {
269      String[] tok = s.split("=", 2);
270      ret.add(new MyEntry(tok[0], tok[1]));
271    }
272
273    return ret;
274  }
275
276  public String toPrettyString() {
277    StringBuilder sb = new StringBuilder();
278
279    for (Entry<String, String> l : toStringPairs()) {
280      sb.append(l.getKey() + "=" + l.getValue() + "\n");
281    }
282    return sb.toString();
283  }
284
285  public void assertStaticClean() throws ClassHierarchyException {
286    if (!(
287        setImpls.isEmpty() &&
288            setImplSets.isEmpty() &&
289            setLateImplSets.isEmpty() &&
290            setParamSets.isEmpty() &&
291            setLateImpls.isEmpty() &&
292            setParams.isEmpty() &&
293            setImplLists.isEmpty() &&
294            setParamLists.isEmpty()
295    )) {
296      throw new ClassHierarchyException("Detected statically set ConfigurationModule Parameter / Implementation.  set() should only be used dynamically.  Use bind...() instead.");
297    }
298  }
299}