protected void tryWriteMetaInf(
      TreeLogger logger, Class<?> cls, JClassType impl, GeneratorContext context) {
    String serviceInterface = cls.getName();
    String serviceImplementation = impl.getQualifiedBinaryName();
    ArrayList<File> outputDirs = new ArrayList<File>();
    PropertyOracle properties = context.getPropertyOracle();
    try {
      File root = new File("");
      if (root.getAbsolutePath().endsWith("war"))
        root = new File(root.getAbsolutePath().replace(separator + "war", "") + separator);
      else root = new File(root.getAbsolutePath());
      ConfigurationProperty output = properties.getConfigurationProperty("xinject.output.dir");
      for (String dir : output.getValues()) {
        File f = new File(root, dir);
        if (f.isDirectory()) {
          outputDirs.add(f);
          f = new File(f, "META-INF" + separator + "services");
          if (!f.exists()) {
            if (!f.mkdirs()) {
              logger.log(
                  Type.WARN,
                  "Unable to create META-INF"
                      + separator
                      + "services "
                      + " in "
                      + f.getAbsolutePath()
                      + " "
                      + "Please ensure this directory exists, and is writable.");
            }
          }
        } else {
          logger.log(
              Type.WARN,
              "Missing xinject output directory: "
                  + f.getAbsolutePath()
                  + ". "
                  + "Please set xinject.output.dir to existing source directories; current value: "
                  + output.getValues());
        }
      }
    } catch (BadPropertyValueException e1) {
      logger.log(Type.WARN, "Unexpected propery exception for xinject.output.dir", e1);
    }
    try {
      String prefix =
          ".."
              + separator
              + "WEB-INF"
              + separator
              + "classes"
              + separator
              + "META-INF"
              + separator
              + "singletons"
              + separator;
      // TODO use a typed artifact to let the linker have a peak if it needs to
      OutputStream res = context.tryCreateResource(logger, prefix + serviceInterface);
      res.write(serviceImplementation.getBytes());
      context.commitResource(logger, res).setVisibility(Visibility.Public);
    } catch (UnableToCompleteException e) {
      logger.log(Type.ERROR, "Couldn't write java services to META-INF/singeltons", e);
    } catch (IOException e) {
      logger.log(
          Type.ERROR,
          "Couldn't write java services to META-INF/singletons; please ensure the war folder has full write access and the disk is not full.",
          e);
      e.printStackTrace();
    }

    logger.log(
        Type.TRACE,
        "Saving META-INF/singletons for " + serviceInterface + " -> " + serviceImplementation);
    exports:
    for (File output : outputDirs) {
      String knownContent = null;
      // check for existing META-INF/singletons entries, so we don't clobber anything
      File existing =
          new File(output, "META-INF" + separator + "services" + separator + serviceInterface);
      logger.log(Type.TRACE, "Saving ServiceLoader descriptor to " + existing.getAbsolutePath());
      if (existing.isFile()) {
        // need to read in existing manifest, and skip if service already exists
        BufferedReader reader = null;
        FileInputStream in = null;
        try {
          in = new FileInputStream(existing);
          reader = new BufferedReader(new InputStreamReader(in));

          String line;
          StringBuilder b = new StringBuilder();
          while ((line = reader.readLine()) != null) {
            if (line.equals(serviceImplementation)) {
              // the service impl already exists; skip to next output dir
              // TODO put in a flag to override top permission.
              try {
                ConfigurationProperty prop =
                    context
                        .getPropertyOracle()
                        .getConfigurationProperty("xinject.overwrite.existing");
                List<String> values = prop.getValues();
                if (values.size() > 0)
                  if (values.get(0).matches("true")) {
                    // if we're supposed to overwrite the value, but it's already on top
                    if (b.length() == 0) {
                      continue exports; // skip the file write
                    }
                    continue; // this erases the existing value so we can put it back over top.
                    // it also skips the breaking-continue below.
                  }
              } catch (BadPropertyValueException e) {
                logger.log(Type.TRACE, "", e);
              }
              // if we've found the service, and are not allowed to move it to top,
              continue exports; // carry on in the loop above.
            }
            b.append(line + "\n");
          }
          knownContent = b.toString().substring(0, b.length() - 1);
        } catch (IOException e) {
          logger.log(
              Type.WARN,
              "Received io exception writing META-INF/service for " + existing.getAbsolutePath());
        } finally {
          try {

            if (in != null) in.close();
            if (reader != null) reader.close();
          } catch (IOException e) {
          }
        }
      }
      // save a new java service descriptor
      FileWriter writer = null;
      try {
        try {
          boolean exists = existing.isFile();
          if (!exists) {
            if (!existing.createNewFile()) {
              logger.log(Type.WARN, "Could not create output file for " + existing);
              continue exports;
            }
          }
          writer = new FileWriter(existing, false);
          if (knownContent == null) {
            writer.append(serviceImplementation);
          } else {
            writer.append(serviceImplementation);
            writer.append('\n');
            writer.append(knownContent);
          }
        } finally {
          if (writer != null) {
            writer.close();
          }
        }
      } catch (IOException e) {
        logger.log(
            Type.WARN,
            "File write exception trying to save META-INF/singletons for " + existing,
            e);
      }
    }
  }