Проблема с XmlDecoder и Akka Stream только внутри контейнера Docker

Я столкнулся с проблемой использования XmlDecoder в AkkaStream только в приложении, работающем в контейнере Docker.


Описание ошибки

java.lang.ClassNotFoundException: com/example/xmldecoder/FileDto
Continuing ...
java.lang.ClassNotFoundException: com/example/xmldecoder/FileDto
Continuing ...
java.lang.NoSuchMethodException: <unbound>=XMLDecoder.new();
Continuing ...
java.lang.NoSuchMethodException: <unbound>=XMLDecoder.new();
Continuing ...
java.lang.IllegalStateException: The outer element does not return value
Continuing ...
java.lang.IllegalStateException: The outer element does not return value
Continuing ...
java.lang.IllegalStateException: The outer element does not return value
Continuing ...
java.lang.IllegalStateException: The outer element does not return value
Continuing ...
2019-05-22 09:42:29.145 ERROR 1 --- [onPool-worker-5] com.example.xmldecoder.FileReader        : Unexpected exception in load file, {} 
java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
    at java.desktop/java.beans.XMLDecoder.readObject(XMLDecoder.java:251) ~[na:na]
    at com.example.xmldecoder.FileReader.lambda$loadFile$0(XmlDecoderApplication.java:66) ~[classes!/:0.0.1-SNAPSHOT]
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700) ~[na:na]
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1692) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177) ~[na:na]

Необходимо выполнить пару условий:

  1. Ошибка возникает только внутри Docker, при запуске кода на неконтейнерном хосте все ок
  2. Проблема только в том, что когда вы используете XmlDecoder, чтение файла построчно с помощью BufferedReader работает нормально.
  3. Когда вы ограничиваете ЦП докера (--cpus=1), ошибка не возникает
  4. Когда вы используете ExecutorService вместо Akka Streams, ошибка не возникает
  5. Я пытался использовать некоторые флаги докеров, которые помогают с проблемами JDK (UseContainerSupport, ActiveProcessorCount), но это не помогло.

Код

Доступен исполняемый пример здесь

Проблемный код ниже:

@Slf4j
@RequiredArgsConstructor
class FileReader {

private final ActorSystem system;
private final ReadJob readJob;

public NotUsed loadFiles() {
    List<String> paths = listFiles(readJob);
    return Source.from(paths)
            .via(Flow.of(String.class).mapAsync(5, p -> loadFile(p)))
            .to(Sink.foreach(System.out::println)).run(ActorMaterializer.create(system));
}

private CompletionStage<String> loadFile(String filePath) {
    return CompletableFuture.supplyAsync(() -> {
        try {
            FileInputStream fis2 = new FileInputStream(filePath);
            BufferedInputStream bis2 = new BufferedInputStream(fis2);
            XMLDecoder xmlDecoder = new XMLDecoder(bis2);
            FileDto mb = (FileDto) xmlDecoder.readObject();
            log.info("Decoder: {}", mb);
            return mb.toString();
        } catch (Exception e) {
            log.error("Unexpected exception in load file, {}", e);
            throw new RuntimeException("Unexpected exception in load file", e);
        }
    });
}

private List<String> listFiles(ReadJob readJob) {
    File folder = new File(readJob.getHolderDirPath().toString());
    File[] listOfFiles = folder.listFiles();
    log.info(listOfFiles.toString());
    return Stream.of(listOfFiles).map(File::getAbsolutePath).collect(Collectors.toList());
}

}

Можно запустить, например, так:

@SpringBootApplication
@EnableScheduling
@Slf4j
public class XmlDecoderApplication {

private Path holderPath = Paths.get("opt", "files_to_load");

public static void main(String[] args) {
    SpringApplication.run(XmlDecoderApplication.class, args);
}

@Scheduled(fixedDelay = 30000, initialDelay = 1000)
public void readFiles() {
    FileReader reader = new FileReader(ActorSystem.create(), new ReadJob(holderPath));
    reader.loadFiles();
}
}

Я полагаю, что основная причина находится где-то между host <-> docker <-> java

Заранее спасибо за любую помощь в этом

я не совсем уверен, что там произошло, но однажды у меня была похожая проблема с чтением ресурса из докера для приложения с akka. проблема там была связана с classloader. если бы я использовал загрузчик классов от akka, приложение не читало ресурс. если я делаю это из основного потока в начале - это сработало. насколько я вижу, XMLDecoder использует некоторый загрузчик классов внутри

Serhii Shynkarenko 22.05.2019 15:28
Основы программирования на Java
Основы программирования на Java
Java - это высокоуровневый объектно-ориентированный язык программирования, основанный на классах.
Концепции JavaScript, которые вы должны знать как JS программист!
Концепции JavaScript, которые вы должны знать как JS программист!
JavaScript (Js) - это язык программирования, объединяющий HTML и CSS с одной из основных технологий Всемирной паутины. Более 97% веб-сайтов используют...
5
1
236
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Код примера работал у меня со следующей модификацией: заменить строку

XMLDecoder xmlDecoder = new XMLDecoder(bis2);

с участием

XMLDecoder xmlDecoder = new XMLDecoder(bis2, null, null, FileDto.class.getClassLoader());

то есть эффективно заставить XMLDecoder использовать точный загрузчик классов, используемый для загрузки рассматриваемого класса. Но что касается того, почему это происходит только

  1. в докере
  2. если --cpus установлено на sth больше 1
  3. с потоками Акка
  4. с определенными версиями JDK

– У меня есть только некоторые (в основном) необоснованные догадки.

Другие вопросы по теме