@Override
  public Map<StackModule, RepoUrlInfoResult> call() throws Exception {

    Type type = new TypeToken<Map<String, Map<String, Object>>>(){}.getType();
    Gson gson = new Gson();

    Map<String, Map<String, Object>> latestUrlMap = null;

    Set<String> ids = new HashSet<>();
    ids.addAll(Collections2.transform(m_stacks, new Function<StackModule, String>() {
      @Override
      public String apply(StackModule input) {
        // TODO Auto-generated method stub
        return new StackId(input.getModuleInfo()).toString();
      }
    }));

    String stackIds = StringUtils.join(ids, ',');

    Long time = System.nanoTime();

    try {
      if (m_uri.getScheme().startsWith("http")) {
        URLStreamProvider streamProvider = new URLStreamProvider(
            LOOKUP_CONNECTION_TIMEOUT, LOOKUP_READ_TIMEOUT,
            null, null, null);

        LOG.info("Loading latest URL info from {} for stacks {}", m_uri, stackIds);

        latestUrlMap = gson.fromJson(new InputStreamReader(
            streamProvider.readFrom(m_uri.toString())), type);
      } else {
        File jsonFile = new File(m_uri);

        if (jsonFile.exists()) {
          LOG.info("Loading latest URL info from file {} for stacks {}", m_uri, stackIds);
          latestUrlMap = gson.fromJson(new FileReader(jsonFile), type);
        }
      }
    } catch (Exception e) {
      LOG.info("Could not load the URI from {}, stack defaults will be used", m_uri);
      throw e;
    } finally {
      LOG.info("Loaded URI {} for stacks {} in {}ms", m_uri, stackIds,
          TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - time));
    }

    Map<StackModule, RepoUrlInfoResult> result = new HashMap<>();

    if (null == latestUrlMap) {
      LOG.error("Could not load latest data for URI {} and stacks {}", m_uri, stackIds);
      return result;
    }

    for (StackModule stackModule : m_stacks) {
      StackId stackId = new StackId(stackModule.getModuleInfo());

      Map<String, Object> map = latestUrlMap.get(stackId.toString());

      if (null == map) {
        continue;
      }

      RepoUrlInfoResult res = new RepoUrlInfoResult();

      if (map.containsKey("manifests")) {
        // versionMap is laid out like [version -> [os -> VDF uri]]
        @SuppressWarnings("unchecked")
        Map<String, Map<String, String>> versionMap = (Map<String, Map<String, String>>) map.get("manifests");

        for (Entry<String, Map<String, String>> versionEntry : versionMap.entrySet()) {
          String version = versionEntry.getKey();
          Map<String, URI> resolvedOsMap = resolveOsMap(stackModule, versionEntry.getValue());

          res.addVersion(version, resolvedOsMap);
        }
      }


      if (map.containsKey("latest-vdf")) {
        @SuppressWarnings("unchecked")
        Map<String, String> osMap = (Map<String, String>) map.get("latest-vdf");

        Map<String, URI> resolvedOsMap = resolveOsMap(stackModule, osMap);

        res.setLatest(resolvedOsMap);
      }

      result.put(stackModule, res);
    }


    return result;

  }
Encapsulates the work to resolve the latest repo information for a stack.
This class must be used AFTER the stack has created its owned repositories.
ambari-server/src/main/java/org/apache/ambari/server/state/stack/RepoUrlInfoCallable.java