Ложка В Миске. Не разрешающий тип CtExecutableReference и объявленный тип в зависимости от среды (Докер и локальная загрузка Spring)

#java #spring-boot #docker #inria-spoon

Вопрос:

Хороший день.

Я столкнулся с очень странной проблемой при использовании библиотеки анализа ложек в моем проекте Java. Я получаю разные результаты синтаксического анализа при запуске одного и того же кода, который использует Spoon Launcher в разных средах

  • 1 Среда — Проект загрузки Spring, который запускается локально из Intellij IDEA
  • 2 Среда — Тот же проект загрузки Spring, который выполняется в контейнере Docker

В локально запущенном проекте Spring Boot — все в порядке, но когда я запускаю тот же код в Docker — я получаю null значения CtExecutableReference.getType() и CtExecutableReference.getDeclaredType()

Я открыл выпуск на GitHub — https://github.com/INRIA/spoon/issues/3926

Вот подробности

Моя версия Spoon-8.2.0. (из репозитория maven)

Я пытаюсь проанализировать (построить AST) код из этого репозитория GitHub, и у меня возникли проблемы с анализом этого класса, здесь есть следующие строки

 ...
@Service
public class ValueServices {
    
    private ValuesRepository valuesRepository;
    
    private Queue<Values> queue;

    @Autowired
    public ValueServices(ValuesRepository valuesRepository) {
        super();
        this.valuesRepository = valuesRepository;
        this.queue = new LinkedList<Values>();
    }
    
    public List<Values> getAllValues() {
        List<Values> values = new ArrayList<>();
        this.valuesRepository.findAll().forEach(values::add);
        return values;
    }

...
}
 

Когда я запускаю анализ и пытаюсь проанализировать CtExecutableReference findAll() метод this.valuesRepository.findAll().forEach(values::add) оператора, я получаю null значения для getType() и getDeclaredType() при запуске моего проекта в Docker.
При локальном запуске оба getType() и getDeclaredType() имеют ненулевые значения

Та же проблема возникает при анализе других аналогичных блоков кода в других проектах. Например, здесь

 @Service
public class BetService {

    public static final String DATE_FORMAT_NOW = "yyyy-MM-dd HH";
    public static final String DATE_FORMAT_NOW_WITH_HOUR_MIN = "yyyy-MM-dd HH:mm:ss";

    private BetRepository betRepository;

    @Autowired
    public BetService(BetRepository betRepository) {
        super();
        this.betRepository = betRepository;
    }

    public List<Bet> getAllBets() {
        List<Bet> bets = new ArrayList<Bet>();
        this.betRepository.findAll().forEach(bets::add);

        return bets;
    }
}
 

Statement with this.betRepository.findAll() has null in both getType and getDeclaredType when running in Docker, but ok in local environment.

At the same time following code is parsed well in both environments

 public class BetRepositoryTest {
    
    @Autowired
    private TestEntityManager entityManager;
 
    @Autowired
    private BetRepository betRepository;

    @Test
    public void test() {
        Bet bet = new Bet("2018-07-06 12:56", "WIN", 103333, 1082, 500.5);
        entityManager.persist(bet);
        entityManager.flush();
       
        List<Bet> bets = betRepository.findByCustomerId(bet.getCustomerId());       
        assertThat(bet.getCustomerId() == bets.get(0).getCustomerId());
    }
}
 

and statement betRepository.findByCustomerId() is parsed ok and has necessary type info both in Docker and n local Spring Boot run.

I double checked local tests — and all is ok — when running code in tests from IDE or starting Spring Boot project from IDE and initialising analysis by calling service from Web UI — it OK and works as expected.

But when I build Docker image — I get null in both type and declaredType.

I’m running Spoon analysis with the following code

 private SourceCodeMetamodel buildMetamodelForFiles(Collection<File> javaFiles) {
        Launcher spoonAPI = new Launcher();
        log.debug("Spoon environment - {}",ToStringBuilder.reflectionToString(spoonAPI.getEnvironment()));
        log.debug("Spoon model builder - {}",ToStringBuilder.reflectionToString(spoonAPI.getModelBuilder()));
        Set<String> inputResources = new HashSet<>();
        for (File javaFile: javaFiles) {
            String javaDir = JavaFileUtils.getJavaFileStorageRootPath(javaFile);
            if (StringUtils.isNotBlank(javaDir) amp;amp; !inputResources.contains(javaDir)) {
                spoonAPI.addInputResource(javaDir);
                inputResources.add(javaDir);
            }
            else if (StringUtils.isBlank(javaDir)) {
                spoonAPI.addInputResource(javaFile.getAbsolutePath());
            }
        }
        spoonAPI.buildModel();
        CtModel ctModel = spoonAPI.getModel();
        Collection<CtType<?>> modelTypes = ctModel.getAllTypes();
        return new SpoonSourceCodeMetamodel(modelTypes,false);
    }
 

Before running Launcher I tried to print it’s settings. Here is what i got

 2021-05-14 13:26:12.329 DEBUG 1 --- [         task-1] c.s.s.r.i.s.SpoonJavaSourceCodeAnalyzer  : Spoon environment - spoon.support.StandardEnvironment@4626a7ce[errorCount=0,processingStopped=false,prettyPrintingMode=FULLYQUALIFIED,warningCount=0,sourceClasspath=<null>,preserveLineNumbers=false,copyResources=true,enableComments=true,level=ERROR,shouldCompile=false,skipSelfChecks=false,complianceLevel=8,previewFeaturesEnabled=false,outputType=classes,noclasspath=true,compressionType=GZIP,sniperMode=false,ignoreDuplicateDeclarations=false,prettyPrinterCreator=<null>,useTabulations=false,tabulationSize=4,binaryOutputDirectory=/spooned-classes]

2021-05-14 13:26:12.333 DEBUG 1 --- [         task-1] c.s.s.r.i.s.SpoonJavaSourceCodeAnalyzer  : Spoon model builder - spoon.support.compiler.jdt.JDTBasedSpoonCompiler@2df98092[environment=<null>,probs=[],requestor=spoon.support.compiler.jdt.TreeBuilderRequestor@57050733,factory=spoon.reflect.factory.FactoryImpl@237d7ffa,javaCompliance=7,sources=<virtual folder>: spoon.support.compiler.VirtualFolder@42618617,templates=<virtual folder>: spoon.support.compiler.VirtualFolder@7fb32ea2,templateClasspath={},compilationUnitFilters=[],sortList=true]
 

I’m running project on Java8 — in both environments (details following).
To build docker I use following commands

 FROM java:8
COPY maven /maven/
ENTRYPOINT java -Xverify:none -XX:TieredStopAtLevel=1 -XX: TieredCompilation -XX: UseSerialGC -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=40 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Djava.security.egd=file:/dev/./urandom -Dspring.profiles.active=${PROFILE:-docker-dev} -jar /maven/skillcounters-sca-service-1.0-SNAPSHOT.jar
 

I tried to switch to different Docker base images (openjdk alpine etc.), but nothing helped.
I tried to exclude all java run options listed above (i.e. like -XXblabla) — didn’t helped either.

To get ideas about what may go wrong i print all environment (including java) data on application start.

Here what is printed for local environment

 Apple_PubSub_Socket_Render : /private/tmp/com.apple.launchd.xCrhs0tTMM/Render
COMMAND_MODE : unix2003
HOME : /Users/sk
JAVA_MAIN_CLASS_66239 : org.codehaus.classworlds.Launcher
JAVA_MAIN_CLASS_66249 : com.skillcounters.sca.SCAServiceApplication
LANG : ru_RU.UTF-8
LC_CTYPE : ru_RU.UTF-8
LOGNAME : sk
PATH : /Users/sk/Develop/d20/db/liquibase:/Users/sk/anaconda/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
PID : 66249
PWD : /Users/sk/Develop/d20/d20-git-repo/skillcounters-sca-service/skillcounters-sca-service-impl
SECURITYSESSIONID : 186a9
SHELL : /bin/bash
SSH_AUTH_SOCK : /private/tmp/com.apple.launchd.ItaWSltKcA/Listeners
TMPDIR : /var/folders/lz/gjd4j2t12_39qs0hpdjqd3sh0000gn/T/
USER : sk
XPC_FLAGS : 0x0
XPC_SERVICE_NAME : com.apple.xpc.launchd.oneshot.0x10000002.idea
__CF_USER_TEXT_ENCODING : 0x1F5:0x0:0x0
awt.toolkit : sun.lwawt.macosx.LWCToolkit
file.encoding : UTF-8
file.encoding.pkg : sun.io
file.separator : /
ftp.nonProxyHosts : local|*.local|169.254/16|*.169.254/16
gopherProxySet : false
http.nonProxyHosts : local|*.local|169.254/16|*.169.254/16
java.awt.graphicsenv : sun.awt.CGraphicsEnvironment
java.awt.headless : true
java.awt.printerjob : sun.lwawt.macosx.CPrinterJob
java.class.path : {all dependent jars go here - excluded them not to pollute issue...}
java.class.version : 52.0
java.endorsed.dirs : /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/endorsed
java.ext.dirs : /Users/sk/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java
java.home : /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre
java.io.tmpdir : /var/folders/lz/gjd4j2t12_39qs0hpdjqd3sh0000gn/T/
java.library.path : /Users/sk/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.
java.runtime.name : Java(TM) SE Runtime Environment
java.runtime.version : 1.8.0_131-b11
java.specification.name : Java Platform API Specification
java.specification.vendor : Oracle Corporation
java.specification.version : 1.8
java.vendor : Oracle Corporation
java.vendor.url : http://java.oracle.com/
java.vendor.url.bug : http://bugreport.sun.com/bugreport/
java.version : 1.8.0_131
java.vm.info : mixed mode
java.vm.name : Java HotSpot(TM) 64-Bit Server VM
java.vm.specification.name : Java Virtual Machine Specification
java.vm.specification.vendor : Oracle Corporation
java.vm.specification.version : 1.8
java.vm.vendor : Oracle Corporation
java.vm.version : 25.131-b11

 

И вот что напечатано в среде Docker

 CA_CERTIFICATES_JAVA_VERSION : 20140324
HOME : /root
HOSTNAME : e297584466e8
JAVA_DEBIAN_VERSION : 8u111-b14-2~bpo8 1
JAVA_HOME : /usr/lib/jvm/java-8-openjdk-amd64
JAVA_VERSION : 8u111
LANG : C.UTF-8
PATH : /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PID : 8
PROFILE : prod
PWD : /
awt.toolkit : sun.awt.X11.XToolkit
file.encoding : UTF-8
file.encoding.pkg : sun.io
file.separator : /
java.awt.graphicsenv : sun.awt.X11GraphicsEnvironment
java.awt.headless : true
java.awt.printerjob : sun.print.PSPrinterJob
java.class.path : /maven/skillcounters-skill-service-1.0-SNAPSHOT.jar
java.class.version : 52.0
java.endorsed.dirs : /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/endorsed
java.ext.dirs : /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext:/usr/java/packages/lib/ext
java.home : /usr/lib/jvm/java-8-openjdk-amd64/jre
java.io.tmpdir : /tmp
java.library.path : /usr/java/packages/lib/amd64:/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib
java.protocol.handler.pkgs : org.springframework.boot.loader
java.runtime.name : OpenJDK Runtime Environment
java.runtime.version : 1.8.0_111-8u111-b14-2~bpo8 1-b14
java.security.egd : file:/dev/./urandom
java.specification.name : Java Platform API Specification
java.specification.vendor : Oracle Corporation
java.specification.version : 1.8
java.vendor : Oracle Corporation
java.vendor.url : http://java.oracle.com/
java.vendor.url.bug : http://bugreport.sun.com/bugreport/
java.version : 1.8.0_111
java.vm.info : mixed mode
java.vm.name : OpenJDK 64-Bit Server VM
java.vm.specification.name : Java Virtual Machine Specification
java.vm.specification.vendor : Oracle Corporation
java.vm.specification.version : 1.8
java.vm.vendor : Oracle Corporation
java.vm.version : 25.111-b14
 

Любая помощь будет признательна

Ответ №1:

Я нашел какую-то подсказку, когда включил ведение журнала Spoon Launcher, установив для него значение «ВСЕ»

Вот что я получил в среде докеров

 ...
The method findAll() is undefined for the type ValuesRepository at /opt/skillcounters/filestorage/github/vipinjo/assignment-consumer/work-tree/d4e050cf3566981ba386e336c5889f8d107abfbb/src/main/java/com/vipinjoseph/assignmentconsumer/service/ValueServices.java:32
 

И в местной среде эти предупреждения не отображаются…

Существует некоторая проблема пути к классу, похожая на https://www.programmersought.com/article/47106442813/

После почти целого дня борьбы с этим я нашел решение

  1. Используйте Spoon 9.0.0 или более поздней версии с этим исправлением
  2. Создайте загрузчик классов с родительским загрузчиком классов из Spoon Launcher
  3. Напрямую включите каждый файл *.jar в URL-адрес загрузчика классов путь к классу (не каталог, содержащий jar, а каждый jar в одном URL-адресе, переданном загрузчику классов)
  4. Установите этот загрузчик классов в качестве загрузчика входных классов
                 URLClassLoader urlClassLoader = URLClassLoader.newInstance(
                                                                jarUrls.toArray(new URL[0])
                                                                ,spoonAPI.getClass().getClassLoader()
                                                );
                spoonAPI.getEnvironment().setInputClassLoader(urlClassLoader);