@VisibleForTesting
  void updateCoders(Iterable<RawErasureCoderFactory> coderFactories) {
    for (RawErasureCoderFactory coderFactory : coderFactories) {
      String codecName = coderFactory.getCodecName();
      List<RawErasureCoderFactory> coders = coderMap.get(codecName);
      if (coders == null) {
        coders = new ArrayList<>();
        coders.add(coderFactory);
        coderMap.put(codecName, coders);
        LOG.debug("Codec registered: codec = {}, coder = {}",
            coderFactory.getCodecName(), coderFactory.getCoderName());
      } else {
        Boolean hasConflit = false;
        for (RawErasureCoderFactory coder : coders) {
          if (coder.getCoderName().equals(coderFactory.getCoderName())) {
            hasConflit = true;
            LOG.error("Coder {} cannot be registered because its coder name " +
                "{} has conflict with {}", coderFactory.getClass().getName(),
                coderFactory.getCoderName(), coder.getClass().getName());
            break;
          }
        }
        if (!hasConflit) {
          // set native coders as default if user does not
          // specify a fallback order
          if (coderFactory instanceof NativeRSRawErasureCoderFactory ||
                  coderFactory instanceof NativeXORRawErasureCoderFactory) {
            coders.add(0, coderFactory);
          } else {
            coders.add(coderFactory);
          }
          LOG.debug("Codec registered: codec = {}, coder = {}",
              coderFactory.getCodecName(), coderFactory.getCoderName());
        }
      }
    }

    // update coderNameMap accordingly
    coderNameMap.clear();
    for (Map.Entry<String, List<RawErasureCoderFactory>> entry :
        coderMap.entrySet()) {
      String codecName = entry.getKey();
      List<RawErasureCoderFactory> coders = entry.getValue();
      coderNameMap.put(codecName, coders.stream().
          map(RawErasureCoderFactory::getCoderName).
          collect(Collectors.toList()).toArray(new String[0]));
      coderNameCompactMap.put(codecName, coders.stream().
          map(RawErasureCoderFactory::getCoderName)
          .collect(Collectors.joining(", ")));
    }
  }
@Test
  public void testUpdateCoders() {
    class RSUserDefinedIncorrectFactory implements RawErasureCoderFactory {
      public RawErasureEncoder createEncoder(ErasureCoderOptions coderOptions) {
        return null;
      }

      public RawErasureDecoder createDecoder(ErasureCoderOptions coderOptions) {
        return null;
      }

      public String getCoderName() {
        return "rs_java";
      }

      public String getCodecName() {
        return ErasureCodeConstants.RS_CODEC_NAME;
      }
    }

    List<RawErasureCoderFactory> userDefinedFactories = new ArrayList<>();
    userDefinedFactories.add(new RSUserDefinedIncorrectFactory());
    CodecRegistry.getInstance().updateCoders(userDefinedFactories);

    // check RS coders
    List<RawErasureCoderFactory> rsCoders = CodecRegistry.getInstance().
        getCoders(ErasureCodeConstants.RS_CODEC_NAME);
    assertEquals(2, rsCoders.size());
    assertTrue(rsCoders.get(0) instanceof NativeRSRawErasureCoderFactory);
    assertTrue(rsCoders.get(1) instanceof RSRawErasureCoderFactory);

    // check RS coder names
    String[] rsCoderNames = CodecRegistry.getInstance().
        getCoderNames(ErasureCodeConstants.RS_CODEC_NAME);
    assertEquals(2, rsCoderNames.length);
    assertEquals(NativeRSRawErasureCoderFactory.CODER_NAME, rsCoderNames[0]);
    assertEquals(RSRawErasureCoderFactory.CODER_NAME, rsCoderNames[1]);
  }