From bc1df36d673cc4f72b10b263fdd3f867eae8e8d6 Mon Sep 17 00:00:00 2001 From: Kirill Shishin Date: Thu, 16 Feb 2023 18:58:59 +0300 Subject: [PATCH 01/13] Introduce Spring beans analyzer project --- settings.gradle.kts | 2 + utbot-spring-analyzer/build.gradle.kts | 15 ++++ .../java/analyzer/SpringBeansAnalyzer.java | 31 ++++++++ .../TestApplicationConfiguration.java | 14 ++++ .../UtBotBeanFactoryPostProcessor.java | 77 +++++++++++++++++++ 5 files changed, 139 insertions(+) create mode 100644 utbot-spring-analyzer/build.gradle.kts create mode 100644 utbot-spring-analyzer/src/main/java/analyzer/SpringBeansAnalyzer.java create mode 100644 utbot-spring-analyzer/src/main/java/analyzer/TestApplicationConfiguration.java create mode 100644 utbot-spring-analyzer/src/main/java/analyzer/UtBotBeanFactoryPostProcessor.java diff --git a/settings.gradle.kts b/settings.gradle.kts index 8337e46250..c80a375ffc 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -69,3 +69,5 @@ if (goIde.split(",").contains(ideType)) { include("utbot-cli-go") include("utbot-intellij-go") } + +include("utbot-spring-analyzer") diff --git a/utbot-spring-analyzer/build.gradle.kts b/utbot-spring-analyzer/build.gradle.kts new file mode 100644 index 0000000000..11adc7e391 --- /dev/null +++ b/utbot-spring-analyzer/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + id("org.springframework.boot") version "2.7.8" + id("io.spring.dependency-management") version "1.1.0" + id("java") +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter") + implementation("org.springframework.boot:spring-boot-starter-web") +} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/java/analyzer/SpringBeansAnalyzer.java b/utbot-spring-analyzer/src/main/java/analyzer/SpringBeansAnalyzer.java new file mode 100644 index 0000000000..2d69c71168 --- /dev/null +++ b/utbot-spring-analyzer/src/main/java/analyzer/SpringBeansAnalyzer.java @@ -0,0 +1,31 @@ +package analyzer; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.ApplicationContextException; + +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; + + +@SpringBootApplication +public class SpringBeansAnalyzer { + + public static void main(String[] args) throws ClassNotFoundException, IOException { + ClassLoader classLoader = new URLClassLoader(new URL[]{Path.of(args[0]).toUri().toURL()}); + Class userConfigurationClass = classLoader.loadClass(args[1]); + + SpringApplication app = new SpringApplicationBuilder(SpringBeansAnalyzer.class) + .sources(TestApplicationConfiguration.class, userConfigurationClass) + .build(); + + try { + app.run(); + } catch (ApplicationContextException e) { + System.out.println("Bean analysis finished successfully"); + } + } +} diff --git a/utbot-spring-analyzer/src/main/java/analyzer/TestApplicationConfiguration.java b/utbot-spring-analyzer/src/main/java/analyzer/TestApplicationConfiguration.java new file mode 100644 index 0000000000..17b324e47c --- /dev/null +++ b/utbot-spring-analyzer/src/main/java/analyzer/TestApplicationConfiguration.java @@ -0,0 +1,14 @@ +package analyzer; + +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class TestApplicationConfiguration { + + @Bean + public static BeanFactoryPostProcessor utBotBeanFactoryPostProcessor() { + return new UtBotBeanFactoryPostProcessor(); + } +} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/java/analyzer/UtBotBeanFactoryPostProcessor.java b/utbot-spring-analyzer/src/main/java/analyzer/UtBotBeanFactoryPostProcessor.java new file mode 100644 index 0000000000..ee0959d45f --- /dev/null +++ b/utbot-spring-analyzer/src/main/java/analyzer/UtBotBeanFactoryPostProcessor.java @@ -0,0 +1,77 @@ +package analyzer; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.core.PriorityOrdered; +import org.springframework.core.type.MethodMetadata; + +import java.io.File; +import java.io.FileWriter; +import java.util.ArrayList; +import java.util.Arrays; + +public class UtBotBeanFactoryPostProcessor implements BeanFactoryPostProcessor, PriorityOrdered { + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + + System.out.println("Started post-processing bean factory in UtBot"); + + ArrayList beanClassNames = new ArrayList<>(); + for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) { + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName); + + if (beanDefinition instanceof AnnotatedBeanDefinition) { + MethodMetadata factoryMethodMetadata = ((AnnotatedBeanDefinition) beanDefinition).getFactoryMethodMetadata(); + if (factoryMethodMetadata != null) { + beanClassNames.add(factoryMethodMetadata.getReturnTypeName()); + } + } else { + String className = beanDefinition.getBeanClassName(); + if (className == null) { + className = beanFactory.getBean(beanDefinitionName).getClass().getName(); + } + beanClassNames.add(className); + } + } + for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) { + BeanDefinitionRegistry beanRegistry = (BeanDefinitionRegistry) beanFactory; + beanRegistry.removeBeanDefinition(beanDefinitionName); + } + + writeToFile(beanClassNames); + } + + private void writeToFile(ArrayList beanClassNames) { + try { + File springBeansFile = new File("SpringBeans.txt"); + FileWriter fileWriter = new FileWriter(springBeansFile); + + Object[] distinctClassNames = beanClassNames.stream() + .distinct() + .toArray(); + Arrays.sort(distinctClassNames); + + for (Object beanClassName : distinctClassNames) { + fileWriter.append(beanClassName.toString()); + fileWriter.append("\n"); + } + + fileWriter.flush(); + fileWriter.close(); + } catch (Throwable e) { + System.out.println("Storing bean information failed"); + } finally { + System.out.println("Finished post-processing bean factory in UtBot"); + } + } + + @Override + public int getOrder() { + return PriorityOrdered.HIGHEST_PRECEDENCE; + } +} \ No newline at end of file From 6b7cae9d15096806ac948f6b72d18fa4b4d880dd Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Wed, 22 Feb 2023 16:33:51 +0300 Subject: [PATCH 02/13] Support PropertySource annotation analysis --- .../java/analyzer/ConfigurationManager.java | 31 +++++++++++++ .../java/analyzer/PropertiesAnalyzer.java | 30 +++++++++++++ .../java/analyzer/SpringAnalysisRunner.java | 43 +++++++++++++++++++ .../java/analyzer/SpringBeansAnalyzer.java | 31 ------------- .../main/resources/fakeapplication.properties | 0 5 files changed, 104 insertions(+), 31 deletions(-) create mode 100644 utbot-spring-analyzer/src/main/java/analyzer/ConfigurationManager.java create mode 100644 utbot-spring-analyzer/src/main/java/analyzer/PropertiesAnalyzer.java create mode 100644 utbot-spring-analyzer/src/main/java/analyzer/SpringAnalysisRunner.java delete mode 100644 utbot-spring-analyzer/src/main/java/analyzer/SpringBeansAnalyzer.java create mode 100644 utbot-spring-analyzer/src/main/resources/fakeapplication.properties diff --git a/utbot-spring-analyzer/src/main/java/analyzer/ConfigurationManager.java b/utbot-spring-analyzer/src/main/java/analyzer/ConfigurationManager.java new file mode 100644 index 0000000000..43a67c42e6 --- /dev/null +++ b/utbot-spring-analyzer/src/main/java/analyzer/ConfigurationManager.java @@ -0,0 +1,31 @@ +package analyzer; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.util.Map; + +public class ConfigurationManager { + + private final ClassLoader classLoader; + private final Class userConfigurationClass; + + public ConfigurationManager(ClassLoader classLoader, Class userConfigurationClass) { + this.classLoader = classLoader; + this.userConfigurationClass = userConfigurationClass; + } + + public void patchPropertySourceAnnotation() throws IllegalAccessException, NoSuchFieldException, ClassNotFoundException { + + Class proxyClass = classLoader.loadClass("java.lang.reflect.Proxy"); + Field hField = proxyClass.getDeclaredField("h"); + hField.setAccessible(true); + + InvocationHandler o = (InvocationHandler) (hField.get(userConfigurationClass.getAnnotations()[2])); + + Class annotationInvocationHandlerClass = classLoader.loadClass("sun.reflect.annotation.AnnotationInvocationHandler"); + Field memberValuesField = annotationInvocationHandlerClass.getDeclaredField("memberValues"); + memberValuesField.setAccessible(true); + Map memberValues = (Map) (memberValuesField.get(o)); + memberValues.put("value", "classpath:fakeapplication.properties"); + } +} diff --git a/utbot-spring-analyzer/src/main/java/analyzer/PropertiesAnalyzer.java b/utbot-spring-analyzer/src/main/java/analyzer/PropertiesAnalyzer.java new file mode 100644 index 0000000000..97bd00afa9 --- /dev/null +++ b/utbot-spring-analyzer/src/main/java/analyzer/PropertiesAnalyzer.java @@ -0,0 +1,30 @@ +package analyzer; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; + +public class PropertiesAnalyzer { + + private final String propertiesFilePath; + + public PropertiesAnalyzer(String propertiesFilePath) { + this.propertiesFilePath = propertiesFilePath; + } + + public ArrayList readProperties() throws IOException { + ArrayList props = new ArrayList<>(); + + BufferedReader reader = new BufferedReader(new FileReader(propertiesFilePath)); + String line = reader.readLine(); + while (line != null) { + props.add(line); + line = reader.readLine(); + } + + reader.close(); + + return props; + } +} diff --git a/utbot-spring-analyzer/src/main/java/analyzer/SpringAnalysisRunner.java b/utbot-spring-analyzer/src/main/java/analyzer/SpringAnalysisRunner.java new file mode 100644 index 0000000000..f67295aef4 --- /dev/null +++ b/utbot-spring-analyzer/src/main/java/analyzer/SpringAnalysisRunner.java @@ -0,0 +1,43 @@ +package analyzer; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.ApplicationContextException; + +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; + +@SpringBootApplication +public class SpringAnalysisRunner { + + public static void main(String[] args) throws ClassNotFoundException, IOException, NoSuchFieldException, IllegalAccessException { + //String arg0 = "D:\\Projects\\spring-starter-lesson-28\\build\\classes\\java\\main"; + //String arg1 = "com.dmdev.spring.config.ApplicationConfiguration"; + //String arg2 = "D:\\Projects\\spring-starter-lesson-28\\build\\resources\\main\\application.properties"; + + ClassLoader classLoader = new URLClassLoader(new URL[]{Path.of(args[0]).toUri().toURL()}); + Class userConfigurationClass = classLoader.loadClass(args[1]); + + ConfigurationManager configurationManager = new ConfigurationManager(classLoader, userConfigurationClass); + PropertiesAnalyzer propertiesAnalyzer = new PropertiesAnalyzer(args[2]); + + configurationManager.patchPropertySourceAnnotation(); + + SpringApplicationBuilder app = new SpringApplicationBuilder(SpringAnalysisRunner.class) + .sources(TestApplicationConfiguration.class, userConfigurationClass); + + for (String prop : propertiesAnalyzer.readProperties()) { + app.properties(prop); + } + + try { + app.build(); + app.run(); + } catch (ApplicationContextException e) { + System.out.println("Bean analysis finished successfully"); + } + } + +} diff --git a/utbot-spring-analyzer/src/main/java/analyzer/SpringBeansAnalyzer.java b/utbot-spring-analyzer/src/main/java/analyzer/SpringBeansAnalyzer.java deleted file mode 100644 index 2d69c71168..0000000000 --- a/utbot-spring-analyzer/src/main/java/analyzer/SpringBeansAnalyzer.java +++ /dev/null @@ -1,31 +0,0 @@ -package analyzer; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.context.ApplicationContextException; - -import java.io.IOException; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Path; - - -@SpringBootApplication -public class SpringBeansAnalyzer { - - public static void main(String[] args) throws ClassNotFoundException, IOException { - ClassLoader classLoader = new URLClassLoader(new URL[]{Path.of(args[0]).toUri().toURL()}); - Class userConfigurationClass = classLoader.loadClass(args[1]); - - SpringApplication app = new SpringApplicationBuilder(SpringBeansAnalyzer.class) - .sources(TestApplicationConfiguration.class, userConfigurationClass) - .build(); - - try { - app.run(); - } catch (ApplicationContextException e) { - System.out.println("Bean analysis finished successfully"); - } - } -} diff --git a/utbot-spring-analyzer/src/main/resources/fakeapplication.properties b/utbot-spring-analyzer/src/main/resources/fakeapplication.properties new file mode 100644 index 0000000000..e69de29bb2 From 42f23703b02ef0a6d25ea03b8f721603c04eda8f Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Wed, 22 Feb 2023 16:59:29 +0300 Subject: [PATCH 03/13] Some corrections is property source support --- .../java/analyzer/ConfigurationManager.java | 30 ++++++++++++++----- .../java/analyzer/SpringAnalysisRunner.java | 2 +- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/utbot-spring-analyzer/src/main/java/analyzer/ConfigurationManager.java b/utbot-spring-analyzer/src/main/java/analyzer/ConfigurationManager.java index 43a67c42e6..b448dd747c 100644 --- a/utbot-spring-analyzer/src/main/java/analyzer/ConfigurationManager.java +++ b/utbot-spring-analyzer/src/main/java/analyzer/ConfigurationManager.java @@ -1,8 +1,13 @@ package analyzer; +import org.springframework.context.annotation.PropertySource; + +import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; +import java.util.Arrays; import java.util.Map; +import java.util.Optional; public class ConfigurationManager { @@ -14,18 +19,27 @@ public ConfigurationManager(ClassLoader classLoader, Class userConfigurationClas this.userConfigurationClass = userConfigurationClass; } - public void patchPropertySourceAnnotation() throws IllegalAccessException, NoSuchFieldException, ClassNotFoundException { + public void patchPropertySourceAnnotation() throws Exception { Class proxyClass = classLoader.loadClass("java.lang.reflect.Proxy"); Field hField = proxyClass.getDeclaredField("h"); hField.setAccessible(true); - InvocationHandler o = (InvocationHandler) (hField.get(userConfigurationClass.getAnnotations()[2])); - - Class annotationInvocationHandlerClass = classLoader.loadClass("sun.reflect.annotation.AnnotationInvocationHandler"); - Field memberValuesField = annotationInvocationHandlerClass.getDeclaredField("memberValues"); - memberValuesField.setAccessible(true); - Map memberValues = (Map) (memberValuesField.get(o)); - memberValues.put("value", "classpath:fakeapplication.properties"); + //Annotation annotationProxy = userConfigurationClass.getAnnotations()[2]; + Optional propertySourceAnnotation = + Arrays.stream(userConfigurationClass.getAnnotations()) + .filter(el -> el.annotationType() == PropertySource.class) + .findFirst(); + if (propertySourceAnnotation.isPresent()) { + InvocationHandler annotationInvocationHandler = (InvocationHandler) (hField.get(propertySourceAnnotation.get())); + + Class annotationInvocationHandlerClass = + classLoader.loadClass("sun.reflect.annotation.AnnotationInvocationHandler"); + Field memberValuesField = annotationInvocationHandlerClass.getDeclaredField("memberValues"); + memberValuesField.setAccessible(true); + + Map memberValues = (Map) (memberValuesField.get(annotationInvocationHandler)); + memberValues.put("value", "classpath:fakeapplication.properties"); + } } } diff --git a/utbot-spring-analyzer/src/main/java/analyzer/SpringAnalysisRunner.java b/utbot-spring-analyzer/src/main/java/analyzer/SpringAnalysisRunner.java index f67295aef4..8064291d08 100644 --- a/utbot-spring-analyzer/src/main/java/analyzer/SpringAnalysisRunner.java +++ b/utbot-spring-analyzer/src/main/java/analyzer/SpringAnalysisRunner.java @@ -12,7 +12,7 @@ @SpringBootApplication public class SpringAnalysisRunner { - public static void main(String[] args) throws ClassNotFoundException, IOException, NoSuchFieldException, IllegalAccessException { + public static void main(String[] args) throws Exception { //String arg0 = "D:\\Projects\\spring-starter-lesson-28\\build\\classes\\java\\main"; //String arg1 = "com.dmdev.spring.config.ApplicationConfiguration"; //String arg2 = "D:\\Projects\\spring-starter-lesson-28\\build\\resources\\main\\application.properties"; From b3db25f7be89c4ef8d41131aadf0f7669ea7a64e Mon Sep 17 00:00:00 2001 From: Kirill Shishin Date: Tue, 28 Feb 2023 03:15:42 +0300 Subject: [PATCH 04/13] Added processing of beans from xml file --- .../java/analyzer/ConfigurationManager.java | 16 +++-- .../java/analyzer/SpringAnalysisRunner.java | 11 +++- .../main/java/analyzer/XmlBeanAnalyzer.java | 66 +++++++++++++++++++ 3 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 utbot-spring-analyzer/src/main/java/analyzer/XmlBeanAnalyzer.java diff --git a/utbot-spring-analyzer/src/main/java/analyzer/ConfigurationManager.java b/utbot-spring-analyzer/src/main/java/analyzer/ConfigurationManager.java index b448dd747c..15cea9bce5 100644 --- a/utbot-spring-analyzer/src/main/java/analyzer/ConfigurationManager.java +++ b/utbot-spring-analyzer/src/main/java/analyzer/ConfigurationManager.java @@ -1,5 +1,6 @@ package analyzer; +import org.springframework.context.annotation.ImportResource; import org.springframework.context.annotation.PropertySource; import java.lang.annotation.Annotation; @@ -19,8 +20,7 @@ public ConfigurationManager(ClassLoader classLoader, Class userConfigurationClas this.userConfigurationClass = userConfigurationClass; } - public void patchPropertySourceAnnotation() throws Exception { - + private void patchAnnotation(Class type, String path) throws Exception { Class proxyClass = classLoader.loadClass("java.lang.reflect.Proxy"); Field hField = proxyClass.getDeclaredField("h"); hField.setAccessible(true); @@ -28,7 +28,7 @@ public void patchPropertySourceAnnotation() throws Exception { //Annotation annotationProxy = userConfigurationClass.getAnnotations()[2]; Optional propertySourceAnnotation = Arrays.stream(userConfigurationClass.getAnnotations()) - .filter(el -> el.annotationType() == PropertySource.class) + .filter(el -> el.annotationType() == type) .findFirst(); if (propertySourceAnnotation.isPresent()) { InvocationHandler annotationInvocationHandler = (InvocationHandler) (hField.get(propertySourceAnnotation.get())); @@ -39,7 +39,15 @@ public void patchPropertySourceAnnotation() throws Exception { memberValuesField.setAccessible(true); Map memberValues = (Map) (memberValuesField.get(annotationInvocationHandler)); - memberValues.put("value", "classpath:fakeapplication.properties"); + memberValues.put("value", path); } } + + public void patchPropertySourceAnnotation() throws Exception { + patchAnnotation(PropertySource.class, "classpath:fakeapplication.properties"); + } + + public void patchImportResourceAnnotation() throws Exception { + patchAnnotation(ImportResource.class, "classpath:fakeapplication.xml"); + } } diff --git a/utbot-spring-analyzer/src/main/java/analyzer/SpringAnalysisRunner.java b/utbot-spring-analyzer/src/main/java/analyzer/SpringAnalysisRunner.java index 8064291d08..56d92abd3f 100644 --- a/utbot-spring-analyzer/src/main/java/analyzer/SpringAnalysisRunner.java +++ b/utbot-spring-analyzer/src/main/java/analyzer/SpringAnalysisRunner.java @@ -4,7 +4,6 @@ import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.ApplicationContextException; -import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Path; @@ -13,17 +12,23 @@ public class SpringAnalysisRunner { public static void main(String[] args) throws Exception { - //String arg0 = "D:\\Projects\\spring-starter-lesson-28\\build\\classes\\java\\main"; + //String arg0 = "/Users/kirillshishin/IdeaProjects/spring-starter-lesson-28/build/classes/java/main"; //String arg1 = "com.dmdev.spring.config.ApplicationConfiguration"; - //String arg2 = "D:\\Projects\\spring-starter-lesson-28\\build\\resources\\main\\application.properties"; + //String arg2 = "/Users/kirillshishin/IdeaProjects/spring-starter-lesson-28/src/main/resources/application.properties"; + //String arg3 = "/Users/kirillshishin/IdeaProjects/spring-starter-lesson-28/src/main/resources/application.xml"; ClassLoader classLoader = new URLClassLoader(new URL[]{Path.of(args[0]).toUri().toURL()}); Class userConfigurationClass = classLoader.loadClass(args[1]); ConfigurationManager configurationManager = new ConfigurationManager(classLoader, userConfigurationClass); PropertiesAnalyzer propertiesAnalyzer = new PropertiesAnalyzer(args[2]); + XmlBeanAnalyzer xmlBeanAnalyzer = new XmlBeanAnalyzer(args[3]); + + xmlBeanAnalyzer.rewriteXmlFile(); + // call action to update project tree configurationManager.patchPropertySourceAnnotation(); + configurationManager.patchImportResourceAnnotation(); SpringApplicationBuilder app = new SpringApplicationBuilder(SpringAnalysisRunner.class) .sources(TestApplicationConfiguration.class, userConfigurationClass); diff --git a/utbot-spring-analyzer/src/main/java/analyzer/XmlBeanAnalyzer.java b/utbot-spring-analyzer/src/main/java/analyzer/XmlBeanAnalyzer.java new file mode 100644 index 0000000000..9a7cfcbb12 --- /dev/null +++ b/utbot-spring-analyzer/src/main/java/analyzer/XmlBeanAnalyzer.java @@ -0,0 +1,66 @@ +package analyzer; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.nio.file.Path; +import java.nio.file.Paths; + + +public class XmlBeanAnalyzer { + //change to auto path detect + private final String PATH = "utbot-spring-analyzer/src/main/resources"; + private final String xmlFilePath; + private final String fakeXmlFilePath; + + + public XmlBeanAnalyzer(String xmlFilePath) { + this.xmlFilePath = xmlFilePath; + this.fakeXmlFilePath = getFakePath(xmlFilePath); + } + + private String getFakePath(String filePath) { + Path path = Paths.get(filePath); + return Paths.get(PATH, "fake" + path.getFileName()).toString(); + } + + public void rewriteXmlFile() throws Exception { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(xmlFilePath); + + deletePropertyPlaceholder(doc); + writeXmlFile(doc); + } + + private void deletePropertyPlaceholder(Document doc) { + NodeList elements = doc.getElementsByTagName("context:property-placeholder"); + int elementsCount = elements.getLength(); + + //https://stackoverflow.com/questions/26618400/how-to-use-multiple-property-placeholder-in-a-spring-xml-file + for (int i = 0; i < elementsCount; i++) { + Element element = (Element) elements.item(i); + element.getParentNode().removeChild(element); + } + doc.normalize(); + } + + private void writeXmlFile(Document doc) throws TransformerException { + Source source = new DOMSource(doc); + Result dest = new StreamResult(fakeXmlFilePath); + + TransformerFactory tFactory = TransformerFactory.newInstance(); + Transformer tFormer = tFactory.newTransformer(); + tFormer.transform(source, dest); + } +} \ No newline at end of file From b96b4153ca4c081927b084ea190268dab1ff1198 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Tue, 28 Feb 2023 14:15:50 +0300 Subject: [PATCH 05/13] Apply some fixes and improve package structure --- .../main/java/analyzer/XmlBeanAnalyzer.java | 66 -------- .../PropertiesAnalyzer.java | 2 +- .../analyzers/XmlConfigurationAnalyzer.java | 61 +++++++ .../SpringAnalysisRunner.java | 29 ++-- .../TestApplicationConfiguration.java | 3 +- .../UtBotBeanFactoryPostProcessor.java | 152 +++++++++--------- .../ConfigurationManager.java | 19 ++- .../src/main/java/utils/ResourceNames.java | 7 + ...properties => fake_application.properties} | 0 9 files changed, 171 insertions(+), 168 deletions(-) delete mode 100644 utbot-spring-analyzer/src/main/java/analyzer/XmlBeanAnalyzer.java rename utbot-spring-analyzer/src/main/java/{analyzer => analyzers}/PropertiesAnalyzer.java (97%) create mode 100644 utbot-spring-analyzer/src/main/java/analyzers/XmlConfigurationAnalyzer.java rename utbot-spring-analyzer/src/main/java/{analyzer => application}/SpringAnalysisRunner.java (59%) rename utbot-spring-analyzer/src/main/java/{analyzer => application}/TestApplicationConfiguration.java (84%) rename utbot-spring-analyzer/src/main/java/{analyzer => post_processors}/UtBotBeanFactoryPostProcessor.java (97%) rename utbot-spring-analyzer/src/main/java/{analyzer => utils}/ConfigurationManager.java (87%) create mode 100644 utbot-spring-analyzer/src/main/java/utils/ResourceNames.java rename utbot-spring-analyzer/src/main/resources/{fakeapplication.properties => fake_application.properties} (100%) diff --git a/utbot-spring-analyzer/src/main/java/analyzer/XmlBeanAnalyzer.java b/utbot-spring-analyzer/src/main/java/analyzer/XmlBeanAnalyzer.java deleted file mode 100644 index 9a7cfcbb12..0000000000 --- a/utbot-spring-analyzer/src/main/java/analyzer/XmlBeanAnalyzer.java +++ /dev/null @@ -1,66 +0,0 @@ -package analyzer; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.transform.Result; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import java.nio.file.Path; -import java.nio.file.Paths; - - -public class XmlBeanAnalyzer { - //change to auto path detect - private final String PATH = "utbot-spring-analyzer/src/main/resources"; - private final String xmlFilePath; - private final String fakeXmlFilePath; - - - public XmlBeanAnalyzer(String xmlFilePath) { - this.xmlFilePath = xmlFilePath; - this.fakeXmlFilePath = getFakePath(xmlFilePath); - } - - private String getFakePath(String filePath) { - Path path = Paths.get(filePath); - return Paths.get(PATH, "fake" + path.getFileName()).toString(); - } - - public void rewriteXmlFile() throws Exception { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document doc = builder.parse(xmlFilePath); - - deletePropertyPlaceholder(doc); - writeXmlFile(doc); - } - - private void deletePropertyPlaceholder(Document doc) { - NodeList elements = doc.getElementsByTagName("context:property-placeholder"); - int elementsCount = elements.getLength(); - - //https://stackoverflow.com/questions/26618400/how-to-use-multiple-property-placeholder-in-a-spring-xml-file - for (int i = 0; i < elementsCount; i++) { - Element element = (Element) elements.item(i); - element.getParentNode().removeChild(element); - } - doc.normalize(); - } - - private void writeXmlFile(Document doc) throws TransformerException { - Source source = new DOMSource(doc); - Result dest = new StreamResult(fakeXmlFilePath); - - TransformerFactory tFactory = TransformerFactory.newInstance(); - Transformer tFormer = tFactory.newTransformer(); - tFormer.transform(source, dest); - } -} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/java/analyzer/PropertiesAnalyzer.java b/utbot-spring-analyzer/src/main/java/analyzers/PropertiesAnalyzer.java similarity index 97% rename from utbot-spring-analyzer/src/main/java/analyzer/PropertiesAnalyzer.java rename to utbot-spring-analyzer/src/main/java/analyzers/PropertiesAnalyzer.java index 97bd00afa9..821171ace6 100644 --- a/utbot-spring-analyzer/src/main/java/analyzer/PropertiesAnalyzer.java +++ b/utbot-spring-analyzer/src/main/java/analyzers/PropertiesAnalyzer.java @@ -1,4 +1,4 @@ -package analyzer; +package analyzers; import java.io.BufferedReader; import java.io.FileReader; diff --git a/utbot-spring-analyzer/src/main/java/analyzers/XmlConfigurationAnalyzer.java b/utbot-spring-analyzer/src/main/java/analyzers/XmlConfigurationAnalyzer.java new file mode 100644 index 0000000000..e732e055bd --- /dev/null +++ b/utbot-spring-analyzer/src/main/java/analyzers/XmlConfigurationAnalyzer.java @@ -0,0 +1,61 @@ +package analyzers; + +import utils.ResourceNames; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +public class XmlConfigurationAnalyzer { + + private final String userXmlFilePath; + + public XmlConfigurationAnalyzer(String userXmlFilePath) { + this.userXmlFilePath = userXmlFilePath; + } + + private final String fakeXmlFilePath = + this.getClass().getClassLoader().getResource(ResourceNames.fakeApplicationXmlFileName).getPath(); + + public void fillFakeApplicationXml() throws Exception { + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document doc = builder.parse(userXmlFilePath); + + // Property placeholders may contain file names relative to user project, + // they will not be found in ours. We import all properties using another approach. + deletePropertyPlaceholders(doc); + + writeXmlFile(doc); + } + + private void deletePropertyPlaceholders(Document doc) { + NodeList elements = doc.getElementsByTagName("context:property-placeholder"); + int elementsCount = elements.getLength(); + + // Xml file may contain several property placeholders: + // see https://stackoverflow.com/questions/26618400/how-to-use-multiple-property-placeholder-in-a-spring-xml-file + for (int i = 0; i < elementsCount; i++) { + Element element = (Element) elements.item(i); + element.getParentNode().removeChild(element); + } + + doc.normalize(); + } + + private void writeXmlFile(Document doc) throws TransformerException { + Transformer tFormer = TransformerFactory.newInstance().newTransformer(); + Source source = new DOMSource(doc); + Result dest = new StreamResult(fakeXmlFilePath); + + tFormer.transform(source, dest); + } +} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/java/analyzer/SpringAnalysisRunner.java b/utbot-spring-analyzer/src/main/java/application/SpringAnalysisRunner.java similarity index 59% rename from utbot-spring-analyzer/src/main/java/analyzer/SpringAnalysisRunner.java rename to utbot-spring-analyzer/src/main/java/application/SpringAnalysisRunner.java index 56d92abd3f..e2a5d46dab 100644 --- a/utbot-spring-analyzer/src/main/java/analyzer/SpringAnalysisRunner.java +++ b/utbot-spring-analyzer/src/main/java/application/SpringAnalysisRunner.java @@ -1,8 +1,11 @@ -package analyzer; +package application; +import analyzers.PropertiesAnalyzer; +import analyzers.XmlConfigurationAnalyzer; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.ApplicationContextException; +import utils.ConfigurationManager; import java.net.URL; import java.net.URLClassLoader; @@ -12,27 +15,25 @@ public class SpringAnalysisRunner { public static void main(String[] args) throws Exception { - //String arg0 = "/Users/kirillshishin/IdeaProjects/spring-starter-lesson-28/build/classes/java/main"; - //String arg1 = "com.dmdev.spring.config.ApplicationConfiguration"; - //String arg2 = "/Users/kirillshishin/IdeaProjects/spring-starter-lesson-28/src/main/resources/application.properties"; - //String arg3 = "/Users/kirillshishin/IdeaProjects/spring-starter-lesson-28/src/main/resources/application.xml"; + String arg0 = "D:/Projects/spring-starter-lesson-28/build/classes/java/main"; + String arg1 = "com.dmdev.spring.config.ApplicationConfiguration"; + String arg2 = "D:/Projects/spring-starter-lesson-28/src/main/resources/application.properties"; + String arg3 = "D:/Projects/spring-starter-lesson-28/src/main/resources/application.xml"; - ClassLoader classLoader = new URLClassLoader(new URL[]{Path.of(args[0]).toUri().toURL()}); - Class userConfigurationClass = classLoader.loadClass(args[1]); + ClassLoader classLoader = new URLClassLoader(new URL[]{Path.of(arg0).toUri().toURL()}); + Class userConfigurationClass = classLoader.loadClass(arg1); ConfigurationManager configurationManager = new ConfigurationManager(classLoader, userConfigurationClass); - PropertiesAnalyzer propertiesAnalyzer = new PropertiesAnalyzer(args[2]); - XmlBeanAnalyzer xmlBeanAnalyzer = new XmlBeanAnalyzer(args[3]); + PropertiesAnalyzer propertiesAnalyzer = new PropertiesAnalyzer(arg2); + XmlConfigurationAnalyzer xmlConfigurationAnalyzer = new XmlConfigurationAnalyzer(arg3); - xmlBeanAnalyzer.rewriteXmlFile(); - // call action to update project tree + xmlConfigurationAnalyzer.fillFakeApplicationXml(); configurationManager.patchPropertySourceAnnotation(); configurationManager.patchImportResourceAnnotation(); - SpringApplicationBuilder app = new SpringApplicationBuilder(SpringAnalysisRunner.class) - .sources(TestApplicationConfiguration.class, userConfigurationClass); - + SpringApplicationBuilder app = new SpringApplicationBuilder(SpringAnalysisRunner.class); + app.sources(TestApplicationConfiguration.class, userConfigurationClass); for (String prop : propertiesAnalyzer.readProperties()) { app.properties(prop); } diff --git a/utbot-spring-analyzer/src/main/java/analyzer/TestApplicationConfiguration.java b/utbot-spring-analyzer/src/main/java/application/TestApplicationConfiguration.java similarity index 84% rename from utbot-spring-analyzer/src/main/java/analyzer/TestApplicationConfiguration.java rename to utbot-spring-analyzer/src/main/java/application/TestApplicationConfiguration.java index 17b324e47c..b4ccd2bfcb 100644 --- a/utbot-spring-analyzer/src/main/java/analyzer/TestApplicationConfiguration.java +++ b/utbot-spring-analyzer/src/main/java/application/TestApplicationConfiguration.java @@ -1,8 +1,9 @@ -package analyzer; +package application; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import post_processors.UtBotBeanFactoryPostProcessor; @Configuration public class TestApplicationConfiguration { diff --git a/utbot-spring-analyzer/src/main/java/analyzer/UtBotBeanFactoryPostProcessor.java b/utbot-spring-analyzer/src/main/java/post_processors/UtBotBeanFactoryPostProcessor.java similarity index 97% rename from utbot-spring-analyzer/src/main/java/analyzer/UtBotBeanFactoryPostProcessor.java rename to utbot-spring-analyzer/src/main/java/post_processors/UtBotBeanFactoryPostProcessor.java index ee0959d45f..6be425f0f5 100644 --- a/utbot-spring-analyzer/src/main/java/analyzer/UtBotBeanFactoryPostProcessor.java +++ b/utbot-spring-analyzer/src/main/java/post_processors/UtBotBeanFactoryPostProcessor.java @@ -1,77 +1,77 @@ -package analyzer; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.core.PriorityOrdered; -import org.springframework.core.type.MethodMetadata; - -import java.io.File; -import java.io.FileWriter; -import java.util.ArrayList; -import java.util.Arrays; - -public class UtBotBeanFactoryPostProcessor implements BeanFactoryPostProcessor, PriorityOrdered { - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - - System.out.println("Started post-processing bean factory in UtBot"); - - ArrayList beanClassNames = new ArrayList<>(); - for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) { - BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName); - - if (beanDefinition instanceof AnnotatedBeanDefinition) { - MethodMetadata factoryMethodMetadata = ((AnnotatedBeanDefinition) beanDefinition).getFactoryMethodMetadata(); - if (factoryMethodMetadata != null) { - beanClassNames.add(factoryMethodMetadata.getReturnTypeName()); - } - } else { - String className = beanDefinition.getBeanClassName(); - if (className == null) { - className = beanFactory.getBean(beanDefinitionName).getClass().getName(); - } - beanClassNames.add(className); - } - } - for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) { - BeanDefinitionRegistry beanRegistry = (BeanDefinitionRegistry) beanFactory; - beanRegistry.removeBeanDefinition(beanDefinitionName); - } - - writeToFile(beanClassNames); - } - - private void writeToFile(ArrayList beanClassNames) { - try { - File springBeansFile = new File("SpringBeans.txt"); - FileWriter fileWriter = new FileWriter(springBeansFile); - - Object[] distinctClassNames = beanClassNames.stream() - .distinct() - .toArray(); - Arrays.sort(distinctClassNames); - - for (Object beanClassName : distinctClassNames) { - fileWriter.append(beanClassName.toString()); - fileWriter.append("\n"); - } - - fileWriter.flush(); - fileWriter.close(); - } catch (Throwable e) { - System.out.println("Storing bean information failed"); - } finally { - System.out.println("Finished post-processing bean factory in UtBot"); - } - } - - @Override - public int getOrder() { - return PriorityOrdered.HIGHEST_PRECEDENCE; - } +package post_processors; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.core.PriorityOrdered; +import org.springframework.core.type.MethodMetadata; + +import java.io.File; +import java.io.FileWriter; +import java.util.ArrayList; +import java.util.Arrays; + +public class UtBotBeanFactoryPostProcessor implements BeanFactoryPostProcessor, PriorityOrdered { + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + + System.out.println("Started post-processing bean factory in UtBot"); + + ArrayList beanClassNames = new ArrayList<>(); + for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) { + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName); + + if (beanDefinition instanceof AnnotatedBeanDefinition) { + MethodMetadata factoryMethodMetadata = ((AnnotatedBeanDefinition) beanDefinition).getFactoryMethodMetadata(); + if (factoryMethodMetadata != null) { + beanClassNames.add(factoryMethodMetadata.getReturnTypeName()); + } + } else { + String className = beanDefinition.getBeanClassName(); + if (className == null) { + className = beanFactory.getBean(beanDefinitionName).getClass().getName(); + } + beanClassNames.add(className); + } + } + for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) { + BeanDefinitionRegistry beanRegistry = (BeanDefinitionRegistry) beanFactory; + beanRegistry.removeBeanDefinition(beanDefinitionName); + } + + writeToFile(beanClassNames); + } + + private void writeToFile(ArrayList beanClassNames) { + try { + File springBeansFile = new File("SpringBeans.txt"); + FileWriter fileWriter = new FileWriter(springBeansFile); + + Object[] distinctClassNames = beanClassNames.stream() + .distinct() + .toArray(); + Arrays.sort(distinctClassNames); + + for (Object beanClassName : distinctClassNames) { + fileWriter.append(beanClassName.toString()); + fileWriter.append("\n"); + } + + fileWriter.flush(); + fileWriter.close(); + } catch (Throwable e) { + System.out.println("Storing bean information failed"); + } finally { + System.out.println("Finished post-processing bean factory in UtBot"); + } + } + + @Override + public int getOrder() { + return PriorityOrdered.HIGHEST_PRECEDENCE; + } } \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/java/analyzer/ConfigurationManager.java b/utbot-spring-analyzer/src/main/java/utils/ConfigurationManager.java similarity index 87% rename from utbot-spring-analyzer/src/main/java/analyzer/ConfigurationManager.java rename to utbot-spring-analyzer/src/main/java/utils/ConfigurationManager.java index 15cea9bce5..04b7557e05 100644 --- a/utbot-spring-analyzer/src/main/java/analyzer/ConfigurationManager.java +++ b/utbot-spring-analyzer/src/main/java/utils/ConfigurationManager.java @@ -1,4 +1,4 @@ -package analyzer; +package utils; import org.springframework.context.annotation.ImportResource; import org.springframework.context.annotation.PropertySource; @@ -20,12 +20,19 @@ public ConfigurationManager(ClassLoader classLoader, Class userConfigurationClas this.userConfigurationClass = userConfigurationClass; } + public void patchPropertySourceAnnotation() throws Exception { + patchAnnotation(PropertySource.class, String.format("classpath:%s", ResourceNames.fakePropertiesFileName)); + } + + public void patchImportResourceAnnotation() throws Exception { + patchAnnotation(ImportResource.class, String.format("classpath:%s", ResourceNames.fakeApplicationXmlFileName)); + } + private void patchAnnotation(Class type, String path) throws Exception { Class proxyClass = classLoader.loadClass("java.lang.reflect.Proxy"); Field hField = proxyClass.getDeclaredField("h"); hField.setAccessible(true); - //Annotation annotationProxy = userConfigurationClass.getAnnotations()[2]; Optional propertySourceAnnotation = Arrays.stream(userConfigurationClass.getAnnotations()) .filter(el -> el.annotationType() == type) @@ -42,12 +49,4 @@ private void patchAnnotation(Class type, String path) throws Exception { memberValues.put("value", path); } } - - public void patchPropertySourceAnnotation() throws Exception { - patchAnnotation(PropertySource.class, "classpath:fakeapplication.properties"); - } - - public void patchImportResourceAnnotation() throws Exception { - patchAnnotation(ImportResource.class, "classpath:fakeapplication.xml"); - } } diff --git a/utbot-spring-analyzer/src/main/java/utils/ResourceNames.java b/utbot-spring-analyzer/src/main/java/utils/ResourceNames.java new file mode 100644 index 0000000000..c0c58c8094 --- /dev/null +++ b/utbot-spring-analyzer/src/main/java/utils/ResourceNames.java @@ -0,0 +1,7 @@ +package utils; + +public class ResourceNames { + + public static final String fakePropertiesFileName = "fake_application.properties"; + public static final String fakeApplicationXmlFileName = "fake_application.xml"; +} diff --git a/utbot-spring-analyzer/src/main/resources/fakeapplication.properties b/utbot-spring-analyzer/src/main/resources/fake_application.properties similarity index 100% rename from utbot-spring-analyzer/src/main/resources/fakeapplication.properties rename to utbot-spring-analyzer/src/main/resources/fake_application.properties From 4cd39cf5158aafde71e3e89d57100894c1ecde67 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Tue, 28 Feb 2023 14:59:13 +0300 Subject: [PATCH 06/13] Little additional fixes --- .../java/application/SpringAnalysisRunner.java | 17 ++++++++--------- .../main/java/utils/ConfigurationManager.java | 7 ++++--- .../src/main/resources/fake_application.xml | 6 ++++++ 3 files changed, 18 insertions(+), 12 deletions(-) create mode 100644 utbot-spring-analyzer/src/main/resources/fake_application.xml diff --git a/utbot-spring-analyzer/src/main/java/application/SpringAnalysisRunner.java b/utbot-spring-analyzer/src/main/java/application/SpringAnalysisRunner.java index e2a5d46dab..fc59567f59 100644 --- a/utbot-spring-analyzer/src/main/java/application/SpringAnalysisRunner.java +++ b/utbot-spring-analyzer/src/main/java/application/SpringAnalysisRunner.java @@ -15,17 +15,17 @@ public class SpringAnalysisRunner { public static void main(String[] args) throws Exception { - String arg0 = "D:/Projects/spring-starter-lesson-28/build/classes/java/main"; - String arg1 = "com.dmdev.spring.config.ApplicationConfiguration"; - String arg2 = "D:/Projects/spring-starter-lesson-28/src/main/resources/application.properties"; - String arg3 = "D:/Projects/spring-starter-lesson-28/src/main/resources/application.xml"; + //String arg0 = "D:/Projects/spring-starter-lesson-28/build/classes/java/main"; + //String arg1 = "com.dmdev.spring.config.ApplicationConfiguration"; + //String arg2 = "D:/Projects/spring-starter-lesson-28/src/main/resources/application.properties"; + //String arg3 = "D:/Projects/spring-starter-lesson-28/src/main/resources/application.xml"; - ClassLoader classLoader = new URLClassLoader(new URL[]{Path.of(arg0).toUri().toURL()}); - Class userConfigurationClass = classLoader.loadClass(arg1); + ClassLoader classLoader = new URLClassLoader(new URL[]{Path.of(args[0]).toUri().toURL()}); + Class userConfigurationClass = classLoader.loadClass(args[1]); ConfigurationManager configurationManager = new ConfigurationManager(classLoader, userConfigurationClass); - PropertiesAnalyzer propertiesAnalyzer = new PropertiesAnalyzer(arg2); - XmlConfigurationAnalyzer xmlConfigurationAnalyzer = new XmlConfigurationAnalyzer(arg3); + PropertiesAnalyzer propertiesAnalyzer = new PropertiesAnalyzer(args[2]); + XmlConfigurationAnalyzer xmlConfigurationAnalyzer = new XmlConfigurationAnalyzer(args[2]); xmlConfigurationAnalyzer.fillFakeApplicationXml(); @@ -45,5 +45,4 @@ public static void main(String[] args) throws Exception { System.out.println("Bean analysis finished successfully"); } } - } diff --git a/utbot-spring-analyzer/src/main/java/utils/ConfigurationManager.java b/utbot-spring-analyzer/src/main/java/utils/ConfigurationManager.java index 04b7557e05..f816671d67 100644 --- a/utbot-spring-analyzer/src/main/java/utils/ConfigurationManager.java +++ b/utbot-spring-analyzer/src/main/java/utils/ConfigurationManager.java @@ -28,15 +28,16 @@ public void patchImportResourceAnnotation() throws Exception { patchAnnotation(ImportResource.class, String.format("classpath:%s", ResourceNames.fakeApplicationXmlFileName)); } - private void patchAnnotation(Class type, String path) throws Exception { + private void patchAnnotation(Class annotationType, String newValue) throws Exception { Class proxyClass = classLoader.loadClass("java.lang.reflect.Proxy"); Field hField = proxyClass.getDeclaredField("h"); hField.setAccessible(true); Optional propertySourceAnnotation = Arrays.stream(userConfigurationClass.getAnnotations()) - .filter(el -> el.annotationType() == type) + .filter(el -> el.annotationType() == annotationType) .findFirst(); + if (propertySourceAnnotation.isPresent()) { InvocationHandler annotationInvocationHandler = (InvocationHandler) (hField.get(propertySourceAnnotation.get())); @@ -46,7 +47,7 @@ private void patchAnnotation(Class type, String path) throws Exception { memberValuesField.setAccessible(true); Map memberValues = (Map) (memberValuesField.get(annotationInvocationHandler)); - memberValues.put("value", path); + memberValues.put("value", newValue); } } } diff --git a/utbot-spring-analyzer/src/main/resources/fake_application.xml b/utbot-spring-analyzer/src/main/resources/fake_application.xml new file mode 100644 index 0000000000..142def25af --- /dev/null +++ b/utbot-spring-analyzer/src/main/resources/fake_application.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file From 208559b9683300e5541fc2904ae535f25cfe706d Mon Sep 17 00:00:00 2001 From: Kirill Shishin Date: Fri, 3 Mar 2023 15:55:31 +0300 Subject: [PATCH 07/13] SpringAnalysis rewritten on kotlin --- .../java/analyzers/PropertiesAnalyzer.java | 30 -------- .../analyzers/XmlConfigurationAnalyzer.java | 61 --------------- .../application/SpringAnalysisRunner.java | 48 ------------ .../TestApplicationConfiguration.java | 15 ---- .../UtBotBeanFactoryPostProcessor.java | 77 ------------------- .../main/java/utils/ConfigurationManager.java | 53 ------------- .../src/main/java/utils/ResourceNames.java | 7 -- .../kotlin/analyzers/PropertiesAnalyzer.kt | 24 ++++++ .../analyzers/XmlConfigurationAnalyzer.kt | 51 ++++++++++++ .../application/SpringAnalysisRunner.kt | 52 +++++++++++++ .../TestApplicationConfiguration.kt | 15 ++++ .../UtBotBeanFactoryPostProcessor.kt | 73 ++++++++++++++++++ .../main/kotlin/utils/ConfigurationManager.kt | 48 ++++++++++++ .../src/main/kotlin/utils/ResourceNames.kt | 6 ++ 14 files changed, 269 insertions(+), 291 deletions(-) delete mode 100644 utbot-spring-analyzer/src/main/java/analyzers/PropertiesAnalyzer.java delete mode 100644 utbot-spring-analyzer/src/main/java/analyzers/XmlConfigurationAnalyzer.java delete mode 100644 utbot-spring-analyzer/src/main/java/application/SpringAnalysisRunner.java delete mode 100644 utbot-spring-analyzer/src/main/java/application/TestApplicationConfiguration.java delete mode 100644 utbot-spring-analyzer/src/main/java/post_processors/UtBotBeanFactoryPostProcessor.java delete mode 100644 utbot-spring-analyzer/src/main/java/utils/ConfigurationManager.java delete mode 100644 utbot-spring-analyzer/src/main/java/utils/ResourceNames.java create mode 100644 utbot-spring-analyzer/src/main/kotlin/analyzers/PropertiesAnalyzer.kt create mode 100644 utbot-spring-analyzer/src/main/kotlin/analyzers/XmlConfigurationAnalyzer.kt create mode 100644 utbot-spring-analyzer/src/main/kotlin/application/SpringAnalysisRunner.kt create mode 100644 utbot-spring-analyzer/src/main/kotlin/application/TestApplicationConfiguration.kt create mode 100644 utbot-spring-analyzer/src/main/kotlin/post_processors/UtBotBeanFactoryPostProcessor.kt create mode 100644 utbot-spring-analyzer/src/main/kotlin/utils/ConfigurationManager.kt create mode 100644 utbot-spring-analyzer/src/main/kotlin/utils/ResourceNames.kt diff --git a/utbot-spring-analyzer/src/main/java/analyzers/PropertiesAnalyzer.java b/utbot-spring-analyzer/src/main/java/analyzers/PropertiesAnalyzer.java deleted file mode 100644 index 821171ace6..0000000000 --- a/utbot-spring-analyzer/src/main/java/analyzers/PropertiesAnalyzer.java +++ /dev/null @@ -1,30 +0,0 @@ -package analyzers; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; -import java.util.ArrayList; - -public class PropertiesAnalyzer { - - private final String propertiesFilePath; - - public PropertiesAnalyzer(String propertiesFilePath) { - this.propertiesFilePath = propertiesFilePath; - } - - public ArrayList readProperties() throws IOException { - ArrayList props = new ArrayList<>(); - - BufferedReader reader = new BufferedReader(new FileReader(propertiesFilePath)); - String line = reader.readLine(); - while (line != null) { - props.add(line); - line = reader.readLine(); - } - - reader.close(); - - return props; - } -} diff --git a/utbot-spring-analyzer/src/main/java/analyzers/XmlConfigurationAnalyzer.java b/utbot-spring-analyzer/src/main/java/analyzers/XmlConfigurationAnalyzer.java deleted file mode 100644 index e732e055bd..0000000000 --- a/utbot-spring-analyzer/src/main/java/analyzers/XmlConfigurationAnalyzer.java +++ /dev/null @@ -1,61 +0,0 @@ -package analyzers; - -import utils.ResourceNames; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.transform.Result; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; - -public class XmlConfigurationAnalyzer { - - private final String userXmlFilePath; - - public XmlConfigurationAnalyzer(String userXmlFilePath) { - this.userXmlFilePath = userXmlFilePath; - } - - private final String fakeXmlFilePath = - this.getClass().getClassLoader().getResource(ResourceNames.fakeApplicationXmlFileName).getPath(); - - public void fillFakeApplicationXml() throws Exception { - DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); - Document doc = builder.parse(userXmlFilePath); - - // Property placeholders may contain file names relative to user project, - // they will not be found in ours. We import all properties using another approach. - deletePropertyPlaceholders(doc); - - writeXmlFile(doc); - } - - private void deletePropertyPlaceholders(Document doc) { - NodeList elements = doc.getElementsByTagName("context:property-placeholder"); - int elementsCount = elements.getLength(); - - // Xml file may contain several property placeholders: - // see https://stackoverflow.com/questions/26618400/how-to-use-multiple-property-placeholder-in-a-spring-xml-file - for (int i = 0; i < elementsCount; i++) { - Element element = (Element) elements.item(i); - element.getParentNode().removeChild(element); - } - - doc.normalize(); - } - - private void writeXmlFile(Document doc) throws TransformerException { - Transformer tFormer = TransformerFactory.newInstance().newTransformer(); - Source source = new DOMSource(doc); - Result dest = new StreamResult(fakeXmlFilePath); - - tFormer.transform(source, dest); - } -} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/java/application/SpringAnalysisRunner.java b/utbot-spring-analyzer/src/main/java/application/SpringAnalysisRunner.java deleted file mode 100644 index fc59567f59..0000000000 --- a/utbot-spring-analyzer/src/main/java/application/SpringAnalysisRunner.java +++ /dev/null @@ -1,48 +0,0 @@ -package application; - -import analyzers.PropertiesAnalyzer; -import analyzers.XmlConfigurationAnalyzer; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.context.ApplicationContextException; -import utils.ConfigurationManager; - -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Path; - -@SpringBootApplication -public class SpringAnalysisRunner { - - public static void main(String[] args) throws Exception { - //String arg0 = "D:/Projects/spring-starter-lesson-28/build/classes/java/main"; - //String arg1 = "com.dmdev.spring.config.ApplicationConfiguration"; - //String arg2 = "D:/Projects/spring-starter-lesson-28/src/main/resources/application.properties"; - //String arg3 = "D:/Projects/spring-starter-lesson-28/src/main/resources/application.xml"; - - ClassLoader classLoader = new URLClassLoader(new URL[]{Path.of(args[0]).toUri().toURL()}); - Class userConfigurationClass = classLoader.loadClass(args[1]); - - ConfigurationManager configurationManager = new ConfigurationManager(classLoader, userConfigurationClass); - PropertiesAnalyzer propertiesAnalyzer = new PropertiesAnalyzer(args[2]); - XmlConfigurationAnalyzer xmlConfigurationAnalyzer = new XmlConfigurationAnalyzer(args[2]); - - xmlConfigurationAnalyzer.fillFakeApplicationXml(); - - configurationManager.patchPropertySourceAnnotation(); - configurationManager.patchImportResourceAnnotation(); - - SpringApplicationBuilder app = new SpringApplicationBuilder(SpringAnalysisRunner.class); - app.sources(TestApplicationConfiguration.class, userConfigurationClass); - for (String prop : propertiesAnalyzer.readProperties()) { - app.properties(prop); - } - - try { - app.build(); - app.run(); - } catch (ApplicationContextException e) { - System.out.println("Bean analysis finished successfully"); - } - } -} diff --git a/utbot-spring-analyzer/src/main/java/application/TestApplicationConfiguration.java b/utbot-spring-analyzer/src/main/java/application/TestApplicationConfiguration.java deleted file mode 100644 index b4ccd2bfcb..0000000000 --- a/utbot-spring-analyzer/src/main/java/application/TestApplicationConfiguration.java +++ /dev/null @@ -1,15 +0,0 @@ -package application; - -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import post_processors.UtBotBeanFactoryPostProcessor; - -@Configuration -public class TestApplicationConfiguration { - - @Bean - public static BeanFactoryPostProcessor utBotBeanFactoryPostProcessor() { - return new UtBotBeanFactoryPostProcessor(); - } -} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/java/post_processors/UtBotBeanFactoryPostProcessor.java b/utbot-spring-analyzer/src/main/java/post_processors/UtBotBeanFactoryPostProcessor.java deleted file mode 100644 index 6be425f0f5..0000000000 --- a/utbot-spring-analyzer/src/main/java/post_processors/UtBotBeanFactoryPostProcessor.java +++ /dev/null @@ -1,77 +0,0 @@ -package post_processors; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.core.PriorityOrdered; -import org.springframework.core.type.MethodMetadata; - -import java.io.File; -import java.io.FileWriter; -import java.util.ArrayList; -import java.util.Arrays; - -public class UtBotBeanFactoryPostProcessor implements BeanFactoryPostProcessor, PriorityOrdered { - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - - System.out.println("Started post-processing bean factory in UtBot"); - - ArrayList beanClassNames = new ArrayList<>(); - for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) { - BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName); - - if (beanDefinition instanceof AnnotatedBeanDefinition) { - MethodMetadata factoryMethodMetadata = ((AnnotatedBeanDefinition) beanDefinition).getFactoryMethodMetadata(); - if (factoryMethodMetadata != null) { - beanClassNames.add(factoryMethodMetadata.getReturnTypeName()); - } - } else { - String className = beanDefinition.getBeanClassName(); - if (className == null) { - className = beanFactory.getBean(beanDefinitionName).getClass().getName(); - } - beanClassNames.add(className); - } - } - for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) { - BeanDefinitionRegistry beanRegistry = (BeanDefinitionRegistry) beanFactory; - beanRegistry.removeBeanDefinition(beanDefinitionName); - } - - writeToFile(beanClassNames); - } - - private void writeToFile(ArrayList beanClassNames) { - try { - File springBeansFile = new File("SpringBeans.txt"); - FileWriter fileWriter = new FileWriter(springBeansFile); - - Object[] distinctClassNames = beanClassNames.stream() - .distinct() - .toArray(); - Arrays.sort(distinctClassNames); - - for (Object beanClassName : distinctClassNames) { - fileWriter.append(beanClassName.toString()); - fileWriter.append("\n"); - } - - fileWriter.flush(); - fileWriter.close(); - } catch (Throwable e) { - System.out.println("Storing bean information failed"); - } finally { - System.out.println("Finished post-processing bean factory in UtBot"); - } - } - - @Override - public int getOrder() { - return PriorityOrdered.HIGHEST_PRECEDENCE; - } -} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/java/utils/ConfigurationManager.java b/utbot-spring-analyzer/src/main/java/utils/ConfigurationManager.java deleted file mode 100644 index f816671d67..0000000000 --- a/utbot-spring-analyzer/src/main/java/utils/ConfigurationManager.java +++ /dev/null @@ -1,53 +0,0 @@ -package utils; - -import org.springframework.context.annotation.ImportResource; -import org.springframework.context.annotation.PropertySource; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationHandler; -import java.util.Arrays; -import java.util.Map; -import java.util.Optional; - -public class ConfigurationManager { - - private final ClassLoader classLoader; - private final Class userConfigurationClass; - - public ConfigurationManager(ClassLoader classLoader, Class userConfigurationClass) { - this.classLoader = classLoader; - this.userConfigurationClass = userConfigurationClass; - } - - public void patchPropertySourceAnnotation() throws Exception { - patchAnnotation(PropertySource.class, String.format("classpath:%s", ResourceNames.fakePropertiesFileName)); - } - - public void patchImportResourceAnnotation() throws Exception { - patchAnnotation(ImportResource.class, String.format("classpath:%s", ResourceNames.fakeApplicationXmlFileName)); - } - - private void patchAnnotation(Class annotationType, String newValue) throws Exception { - Class proxyClass = classLoader.loadClass("java.lang.reflect.Proxy"); - Field hField = proxyClass.getDeclaredField("h"); - hField.setAccessible(true); - - Optional propertySourceAnnotation = - Arrays.stream(userConfigurationClass.getAnnotations()) - .filter(el -> el.annotationType() == annotationType) - .findFirst(); - - if (propertySourceAnnotation.isPresent()) { - InvocationHandler annotationInvocationHandler = (InvocationHandler) (hField.get(propertySourceAnnotation.get())); - - Class annotationInvocationHandlerClass = - classLoader.loadClass("sun.reflect.annotation.AnnotationInvocationHandler"); - Field memberValuesField = annotationInvocationHandlerClass.getDeclaredField("memberValues"); - memberValuesField.setAccessible(true); - - Map memberValues = (Map) (memberValuesField.get(annotationInvocationHandler)); - memberValues.put("value", newValue); - } - } -} diff --git a/utbot-spring-analyzer/src/main/java/utils/ResourceNames.java b/utbot-spring-analyzer/src/main/java/utils/ResourceNames.java deleted file mode 100644 index c0c58c8094..0000000000 --- a/utbot-spring-analyzer/src/main/java/utils/ResourceNames.java +++ /dev/null @@ -1,7 +0,0 @@ -package utils; - -public class ResourceNames { - - public static final String fakePropertiesFileName = "fake_application.properties"; - public static final String fakeApplicationXmlFileName = "fake_application.xml"; -} diff --git a/utbot-spring-analyzer/src/main/kotlin/analyzers/PropertiesAnalyzer.kt b/utbot-spring-analyzer/src/main/kotlin/analyzers/PropertiesAnalyzer.kt new file mode 100644 index 0000000000..805dac7e6a --- /dev/null +++ b/utbot-spring-analyzer/src/main/kotlin/analyzers/PropertiesAnalyzer.kt @@ -0,0 +1,24 @@ +package analyzers + +import java.io.BufferedReader +import java.io.FileReader +import java.io.IOException + +class PropertiesAnalyzer(private val propertiesFilePath: String) { + + @Throws(IOException::class) + fun readProperties(): ArrayList { + val props = ArrayList() + + val reader = BufferedReader(FileReader(propertiesFilePath)) + var line = reader.readLine() + while (line != null) { + props.add(line) + line = reader.readLine() + } + + reader.close() + + return props + } +} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/analyzers/XmlConfigurationAnalyzer.kt b/utbot-spring-analyzer/src/main/kotlin/analyzers/XmlConfigurationAnalyzer.kt new file mode 100644 index 0000000000..734fa99d0f --- /dev/null +++ b/utbot-spring-analyzer/src/main/kotlin/analyzers/XmlConfigurationAnalyzer.kt @@ -0,0 +1,51 @@ +package analyzers + +import org.w3c.dom.Document +import org.w3c.dom.Element +import utils.ResourceNames +import javax.xml.parsers.DocumentBuilderFactory +import javax.xml.transform.Result +import javax.xml.transform.Source +import javax.xml.transform.TransformerException +import javax.xml.transform.TransformerFactory +import javax.xml.transform.dom.DOMSource +import javax.xml.transform.stream.StreamResult + +class XmlConfigurationAnalyzer(private val userXmlFilePath: String) { + private val fakeXmlFilePath = this.javaClass.classLoader.getResource(ResourceNames.fakeApplicationXmlFileName)?.path + ?: error("The path must exist") + + @Throws(Exception::class) + fun fillFakeApplicationXml() { + val builder = DocumentBuilderFactory.newInstance().newDocumentBuilder() + val doc = builder.parse(userXmlFilePath) + + // Property placeholders may contain file names relative to user project, + // they will not be found in ours. We import all properties using another approach. + deletePropertyPlaceholders(doc) + writeXmlFile(doc) + } + + private fun deletePropertyPlaceholders(doc: Document) { + val elements = doc.getElementsByTagName("context:property-placeholder") + val elementsCount = elements.length + + // Xml file may contain several property placeholders: + // see https://stackoverflow.com/questions/26618400/how-to-use-multiple-property-placeholder-in-a-spring-xml-file + for (i in 0 until elementsCount) { + val element = elements.item(i) as Element + element.parentNode.removeChild(element) + } + + doc.normalize() + } + + @Throws(TransformerException::class) + private fun writeXmlFile(doc: Document) { + val tFormer = TransformerFactory.newInstance().newTransformer() + val source: Source = DOMSource(doc) + val dest: Result = StreamResult(fakeXmlFilePath) + + tFormer.transform(source, dest) + } +} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/application/SpringAnalysisRunner.kt b/utbot-spring-analyzer/src/main/kotlin/application/SpringAnalysisRunner.kt new file mode 100644 index 0000000000..7385d1f150 --- /dev/null +++ b/utbot-spring-analyzer/src/main/kotlin/application/SpringAnalysisRunner.kt @@ -0,0 +1,52 @@ +package application + +import analyzers.PropertiesAnalyzer +import analyzers.XmlConfigurationAnalyzer +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.builder.SpringApplicationBuilder +import org.springframework.context.ApplicationContextException +import utils.ConfigurationManager +import java.net.URLClassLoader +import java.nio.file.Path + +@SpringBootApplication +open class SpringAnalysisRunner { + + fun main(args: Array) { + //val arg0 = "D:/Projects/spring-starter-lesson-28/build/classes/java/main"; + //val arg1 = "com.dmdev.spring.config.ApplicationConfiguration"; + //val arg2 = "D:/Projects/spring-starter-lesson-28/src/main/resources/application.properties"; + //val arg3 = "D:/Projects/spring-starter-lesson-28/src/main/resources/application.xml"; + + val classLoader: ClassLoader = URLClassLoader(arrayOf(Path.of(args[0]).toUri().toURL())) + val userConfigurationClass = classLoader.loadClass(args[1]) + + val configurationManager = ConfigurationManager(classLoader, userConfigurationClass) + val propertiesAnalyzer = PropertiesAnalyzer(args[2]) + val xmlConfigurationAnalyzer = XmlConfigurationAnalyzer(args[3]) + + xmlConfigurationAnalyzer.fillFakeApplicationXml() + + configurationManager.patchPropertySourceAnnotation() + configurationManager.patchImportResourceAnnotation() + + val app = SpringApplicationBuilder(SpringAnalysisRunner::class.java) + app.sources(TestApplicationConfiguration::class.java, userConfigurationClass) + for (prop in propertiesAnalyzer.readProperties()) { + app.properties(prop) + } + + try { + app.build() + app.run() + } catch (e: ApplicationContextException) { + println("Bean analysis finished successfully") + } + } +} + + +fun main(args: Array) { + val name = SpringAnalysisRunner() + name.main(args) +} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/application/TestApplicationConfiguration.kt b/utbot-spring-analyzer/src/main/kotlin/application/TestApplicationConfiguration.kt new file mode 100644 index 0000000000..cfb7732525 --- /dev/null +++ b/utbot-spring-analyzer/src/main/kotlin/application/TestApplicationConfiguration.kt @@ -0,0 +1,15 @@ +package application + +import org.springframework.beans.factory.config.BeanFactoryPostProcessor +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import post_processors.UtBotBeanFactoryPostProcessor + +@Configuration +open class TestApplicationConfiguration { + + @Bean + open fun utBotBeanFactoryPostProcessor(): BeanFactoryPostProcessor { + return UtBotBeanFactoryPostProcessor() + } +} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/post_processors/UtBotBeanFactoryPostProcessor.kt b/utbot-spring-analyzer/src/main/kotlin/post_processors/UtBotBeanFactoryPostProcessor.kt new file mode 100644 index 0000000000..6784688f4a --- /dev/null +++ b/utbot-spring-analyzer/src/main/kotlin/post_processors/UtBotBeanFactoryPostProcessor.kt @@ -0,0 +1,73 @@ +package post_processors + +import org.springframework.beans.BeansException +import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition +import org.springframework.beans.factory.config.BeanFactoryPostProcessor +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory +import org.springframework.beans.factory.support.BeanDefinitionRegistry +import org.springframework.core.PriorityOrdered + +import java.io.File +import java.io.FileWriter +import java.util.Arrays + +class UtBotBeanFactoryPostProcessor : BeanFactoryPostProcessor, PriorityOrdered { + + @Throws(BeansException::class) + override fun postProcessBeanFactory(beanFactory: ConfigurableListableBeanFactory) { + + println("Started post-processing bean factory in UtBot") + + val beanClassNames = ArrayList() + for (beanDefinitionName in beanFactory.beanDefinitionNames) { + val beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName) + + if (beanDefinition is AnnotatedBeanDefinition) { + val factoryMethodMetadata = beanDefinition.factoryMethodMetadata + if (factoryMethodMetadata != null) { + beanClassNames.add(factoryMethodMetadata.returnTypeName) + } + } else { + var className = beanDefinition.beanClassName + if (className == null) { + className = beanFactory.getBean(beanDefinitionName).javaClass.name + } + className?.let { beanClassNames.add(it) } + } + } + for (beanDefinitionName in beanFactory.beanDefinitionNames) { + val beanRegistry = beanFactory as BeanDefinitionRegistry + beanRegistry.removeBeanDefinition(beanDefinitionName) + } + + writeToFile(beanClassNames) + } + + private fun writeToFile(beanClassNames: ArrayList) { + try { + val springBeansFile = File("SpringBeans.txt") + val fileWriter = FileWriter(springBeansFile) + + val distinctClassNames = beanClassNames.stream() + .distinct() + .toArray() + Arrays.sort(distinctClassNames) + + for (beanClassName in distinctClassNames) { + fileWriter.append(beanClassName.toString()) + fileWriter.append("\n") + } + + fileWriter.flush() + fileWriter.close() + } catch (e: Throwable) { + println("Storing bean information failed") + } finally { + println("Finished post-processing bean factory in UtBot") + } + } + + override fun getOrder(): Int { + return PriorityOrdered.HIGHEST_PRECEDENCE + } +} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/utils/ConfigurationManager.kt b/utbot-spring-analyzer/src/main/kotlin/utils/ConfigurationManager.kt new file mode 100644 index 0000000000..2c12b35301 --- /dev/null +++ b/utbot-spring-analyzer/src/main/kotlin/utils/ConfigurationManager.kt @@ -0,0 +1,48 @@ +package utils + +import org.springframework.context.annotation.ImportResource +import org.springframework.context.annotation.PropertySource +import java.lang.reflect.InvocationHandler +import java.util.Arrays +import kotlin.reflect.KClass + +class ConfigurationManager(private val classLoader: ClassLoader, private val userConfigurationClass: Class<*>) { + + @Throws(Exception::class) + fun patchPropertySourceAnnotation() { + patchAnnotation(PropertySource::class, String.format("classpath:%s", ResourceNames.fakePropertiesFileName)) + } + + @Throws(Exception::class) + fun patchImportResourceAnnotation() { + patchAnnotation( + ImportResource::class, + String.format("classpath:%s", ResourceNames.fakeApplicationXmlFileName) + ) + } + + @Throws(Exception::class) + private fun patchAnnotation(annotationClass: KClass<*>, newValue: String) { + val proxyClass = classLoader.loadClass("java.lang.reflect.Proxy") + val hField = proxyClass.getDeclaredField("h") + hField.isAccessible = true + + val propertySourceAnnotation = Arrays.stream( + userConfigurationClass.annotations + ) + .filter { el: Annotation -> el.annotationClass == annotationClass } + .findFirst() + + if (propertySourceAnnotation.isPresent) { + val annotationInvocationHandler = hField[propertySourceAnnotation.get()] as InvocationHandler + + val annotationInvocationHandlerClass = + classLoader.loadClass("sun.reflect.annotation.AnnotationInvocationHandler") + val memberValuesField = annotationInvocationHandlerClass.getDeclaredField("memberValues") + memberValuesField.isAccessible = true + + val memberValues = memberValuesField[annotationInvocationHandler] as MutableMap + memberValues["value"] = newValue + } + } +} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/utils/ResourceNames.kt b/utbot-spring-analyzer/src/main/kotlin/utils/ResourceNames.kt new file mode 100644 index 0000000000..e6bc27b544 --- /dev/null +++ b/utbot-spring-analyzer/src/main/kotlin/utils/ResourceNames.kt @@ -0,0 +1,6 @@ +package utils + +object ResourceNames { + const val fakePropertiesFileName = "fake_application.properties" + const val fakeApplicationXmlFileName = "fake_application.xml" +} \ No newline at end of file From 145aa43e5318ebcdb1191a26571a967ea3806b00 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Mon, 6 Mar 2023 18:17:42 +0300 Subject: [PATCH 08/13] Little cosmetic changes --- .../src/main/kotlin/analyzers/PropertiesAnalyzer.kt | 2 -- .../main/kotlin/analyzers/XmlConfigurationAnalyzer.kt | 11 +++-------- .../main/kotlin/application/SpringAnalysisRunner.kt | 1 - .../post_processors/UtBotBeanFactoryPostProcessor.kt | 2 -- .../src/main/kotlin/utils/ConfigurationManager.kt | 9 ++------- 5 files changed, 5 insertions(+), 20 deletions(-) diff --git a/utbot-spring-analyzer/src/main/kotlin/analyzers/PropertiesAnalyzer.kt b/utbot-spring-analyzer/src/main/kotlin/analyzers/PropertiesAnalyzer.kt index 805dac7e6a..32179429a0 100644 --- a/utbot-spring-analyzer/src/main/kotlin/analyzers/PropertiesAnalyzer.kt +++ b/utbot-spring-analyzer/src/main/kotlin/analyzers/PropertiesAnalyzer.kt @@ -5,8 +5,6 @@ import java.io.FileReader import java.io.IOException class PropertiesAnalyzer(private val propertiesFilePath: String) { - - @Throws(IOException::class) fun readProperties(): ArrayList { val props = ArrayList() diff --git a/utbot-spring-analyzer/src/main/kotlin/analyzers/XmlConfigurationAnalyzer.kt b/utbot-spring-analyzer/src/main/kotlin/analyzers/XmlConfigurationAnalyzer.kt index 734fa99d0f..b870d93a1e 100644 --- a/utbot-spring-analyzer/src/main/kotlin/analyzers/XmlConfigurationAnalyzer.kt +++ b/utbot-spring-analyzer/src/main/kotlin/analyzers/XmlConfigurationAnalyzer.kt @@ -4,9 +4,6 @@ import org.w3c.dom.Document import org.w3c.dom.Element import utils.ResourceNames import javax.xml.parsers.DocumentBuilderFactory -import javax.xml.transform.Result -import javax.xml.transform.Source -import javax.xml.transform.TransformerException import javax.xml.transform.TransformerFactory import javax.xml.transform.dom.DOMSource import javax.xml.transform.stream.StreamResult @@ -15,7 +12,6 @@ class XmlConfigurationAnalyzer(private val userXmlFilePath: String) { private val fakeXmlFilePath = this.javaClass.classLoader.getResource(ResourceNames.fakeApplicationXmlFileName)?.path ?: error("The path must exist") - @Throws(Exception::class) fun fillFakeApplicationXml() { val builder = DocumentBuilderFactory.newInstance().newDocumentBuilder() val doc = builder.parse(userXmlFilePath) @@ -40,12 +36,11 @@ class XmlConfigurationAnalyzer(private val userXmlFilePath: String) { doc.normalize() } - @Throws(TransformerException::class) private fun writeXmlFile(doc: Document) { val tFormer = TransformerFactory.newInstance().newTransformer() - val source: Source = DOMSource(doc) - val dest: Result = StreamResult(fakeXmlFilePath) + val source = DOMSource(doc) + val destination = StreamResult(fakeXmlFilePath) - tFormer.transform(source, dest) + tFormer.transform(source, destination) } } \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/application/SpringAnalysisRunner.kt b/utbot-spring-analyzer/src/main/kotlin/application/SpringAnalysisRunner.kt index 7385d1f150..dc0c5c468b 100644 --- a/utbot-spring-analyzer/src/main/kotlin/application/SpringAnalysisRunner.kt +++ b/utbot-spring-analyzer/src/main/kotlin/application/SpringAnalysisRunner.kt @@ -45,7 +45,6 @@ open class SpringAnalysisRunner { } } - fun main(args: Array) { val name = SpringAnalysisRunner() name.main(args) diff --git a/utbot-spring-analyzer/src/main/kotlin/post_processors/UtBotBeanFactoryPostProcessor.kt b/utbot-spring-analyzer/src/main/kotlin/post_processors/UtBotBeanFactoryPostProcessor.kt index 6784688f4a..1866fddcf9 100644 --- a/utbot-spring-analyzer/src/main/kotlin/post_processors/UtBotBeanFactoryPostProcessor.kt +++ b/utbot-spring-analyzer/src/main/kotlin/post_processors/UtBotBeanFactoryPostProcessor.kt @@ -13,9 +13,7 @@ import java.util.Arrays class UtBotBeanFactoryPostProcessor : BeanFactoryPostProcessor, PriorityOrdered { - @Throws(BeansException::class) override fun postProcessBeanFactory(beanFactory: ConfigurableListableBeanFactory) { - println("Started post-processing bean factory in UtBot") val beanClassNames = ArrayList() diff --git a/utbot-spring-analyzer/src/main/kotlin/utils/ConfigurationManager.kt b/utbot-spring-analyzer/src/main/kotlin/utils/ConfigurationManager.kt index 2c12b35301..1da73139e0 100644 --- a/utbot-spring-analyzer/src/main/kotlin/utils/ConfigurationManager.kt +++ b/utbot-spring-analyzer/src/main/kotlin/utils/ConfigurationManager.kt @@ -8,20 +8,15 @@ import kotlin.reflect.KClass class ConfigurationManager(private val classLoader: ClassLoader, private val userConfigurationClass: Class<*>) { - @Throws(Exception::class) - fun patchPropertySourceAnnotation() { + fun patchPropertySourceAnnotation() = patchAnnotation(PropertySource::class, String.format("classpath:%s", ResourceNames.fakePropertiesFileName)) - } - @Throws(Exception::class) - fun patchImportResourceAnnotation() { + fun patchImportResourceAnnotation() = patchAnnotation( ImportResource::class, String.format("classpath:%s", ResourceNames.fakeApplicationXmlFileName) ) - } - @Throws(Exception::class) private fun patchAnnotation(annotationClass: KClass<*>, newValue: String) { val proxyClass = classLoader.loadClass("java.lang.reflect.Proxy") val hField = proxyClass.getDeclaredField("h") From 69b803bcd04bcff5b74d3da758bb679a3d417470 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Mon, 6 Mar 2023 19:34:02 +0300 Subject: [PATCH 09/13] Separate parsing ans analysis --- .../src/main/kotlin/ApplicationRunner.kt | 22 ++++++++ .../analyzers/SpringApplicationAnalyzer.kt | 48 +++++++++++++++++ .../application/SpringAnalysisRunner.kt | 51 ------------------- .../TestApplicationConfiguration.kt | 2 +- 4 files changed, 71 insertions(+), 52 deletions(-) create mode 100644 utbot-spring-analyzer/src/main/kotlin/ApplicationRunner.kt create mode 100644 utbot-spring-analyzer/src/main/kotlin/analyzers/SpringApplicationAnalyzer.kt delete mode 100644 utbot-spring-analyzer/src/main/kotlin/application/SpringAnalysisRunner.kt rename utbot-spring-analyzer/src/main/kotlin/{application => config}/TestApplicationConfiguration.kt (95%) diff --git a/utbot-spring-analyzer/src/main/kotlin/ApplicationRunner.kt b/utbot-spring-analyzer/src/main/kotlin/ApplicationRunner.kt new file mode 100644 index 0000000000..5b1f76425d --- /dev/null +++ b/utbot-spring-analyzer/src/main/kotlin/ApplicationRunner.kt @@ -0,0 +1,22 @@ +package application + +import analyzers.SpringApplicationAnalyzer + +fun main(args: Array) { + /* + val arg0 = "D:/Projects/spring-starter-lesson-28/build/classes/java/main"; + val arg1 = "com.dmdev.spring.config.ApplicationConfiguration"; + val arg2 = "D:/Projects/spring-starter-lesson-28/src/main/resources/application.properties"; + val arg3 = "D:/Projects/spring-starter-lesson-28/src/main/resources/application.xml"; + val springApplicationAnalyzer = SpringApplicationAnalyzer(arg0, arg1, arg2, arg3) + */ + + val springApplicationAnalyzer = SpringApplicationAnalyzer( + applicationPath = args[0], + configurationClassFqn = args[1], + propertyFilePath = args[2], + xmlConfigurationPath = args[3], + ) + + springApplicationAnalyzer.analyze() +} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/analyzers/SpringApplicationAnalyzer.kt b/utbot-spring-analyzer/src/main/kotlin/analyzers/SpringApplicationAnalyzer.kt new file mode 100644 index 0000000000..2345ffe69d --- /dev/null +++ b/utbot-spring-analyzer/src/main/kotlin/analyzers/SpringApplicationAnalyzer.kt @@ -0,0 +1,48 @@ +package analyzers + +import config.TestApplicationConfiguration +import org.springframework.boot.builder.SpringApplicationBuilder +import org.springframework.context.ApplicationContextException +import utils.ConfigurationManager +import java.net.URL +import java.net.URLClassLoader +import java.nio.file.Path + + +class SpringApplicationAnalyzer( + private val applicationPath: String, + private val configurationClassFqn: String, + private val propertyFilePath: String, + private val xmlConfigurationPath: String, +) { + + private val applicationUrl: URL + get() = Path.of(applicationPath).toUri().toURL() + + fun analyze() { + val classLoader: ClassLoader = URLClassLoader(arrayOf(applicationUrl)) + val userConfigurationClass = classLoader.loadClass(configurationClassFqn) + + val configurationManager = ConfigurationManager(classLoader, userConfigurationClass) + val propertiesAnalyzer = PropertiesAnalyzer(propertyFilePath) + val xmlConfigurationAnalyzer = XmlConfigurationAnalyzer(xmlConfigurationPath) + + xmlConfigurationAnalyzer.fillFakeApplicationXml() + + configurationManager.patchPropertySourceAnnotation() + configurationManager.patchImportResourceAnnotation() + + val app = SpringApplicationBuilder(SpringApplicationAnalyzer::class.java) + app.sources(TestApplicationConfiguration::class.java, userConfigurationClass) + for (prop in propertiesAnalyzer.readProperties()) { + app.properties(prop) + } + + try { + app.build() + app.run() + } catch (e: ApplicationContextException) { + println("Bean analysis finished successfully") + } + } +} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/application/SpringAnalysisRunner.kt b/utbot-spring-analyzer/src/main/kotlin/application/SpringAnalysisRunner.kt deleted file mode 100644 index dc0c5c468b..0000000000 --- a/utbot-spring-analyzer/src/main/kotlin/application/SpringAnalysisRunner.kt +++ /dev/null @@ -1,51 +0,0 @@ -package application - -import analyzers.PropertiesAnalyzer -import analyzers.XmlConfigurationAnalyzer -import org.springframework.boot.autoconfigure.SpringBootApplication -import org.springframework.boot.builder.SpringApplicationBuilder -import org.springframework.context.ApplicationContextException -import utils.ConfigurationManager -import java.net.URLClassLoader -import java.nio.file.Path - -@SpringBootApplication -open class SpringAnalysisRunner { - - fun main(args: Array) { - //val arg0 = "D:/Projects/spring-starter-lesson-28/build/classes/java/main"; - //val arg1 = "com.dmdev.spring.config.ApplicationConfiguration"; - //val arg2 = "D:/Projects/spring-starter-lesson-28/src/main/resources/application.properties"; - //val arg3 = "D:/Projects/spring-starter-lesson-28/src/main/resources/application.xml"; - - val classLoader: ClassLoader = URLClassLoader(arrayOf(Path.of(args[0]).toUri().toURL())) - val userConfigurationClass = classLoader.loadClass(args[1]) - - val configurationManager = ConfigurationManager(classLoader, userConfigurationClass) - val propertiesAnalyzer = PropertiesAnalyzer(args[2]) - val xmlConfigurationAnalyzer = XmlConfigurationAnalyzer(args[3]) - - xmlConfigurationAnalyzer.fillFakeApplicationXml() - - configurationManager.patchPropertySourceAnnotation() - configurationManager.patchImportResourceAnnotation() - - val app = SpringApplicationBuilder(SpringAnalysisRunner::class.java) - app.sources(TestApplicationConfiguration::class.java, userConfigurationClass) - for (prop in propertiesAnalyzer.readProperties()) { - app.properties(prop) - } - - try { - app.build() - app.run() - } catch (e: ApplicationContextException) { - println("Bean analysis finished successfully") - } - } -} - -fun main(args: Array) { - val name = SpringAnalysisRunner() - name.main(args) -} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/application/TestApplicationConfiguration.kt b/utbot-spring-analyzer/src/main/kotlin/config/TestApplicationConfiguration.kt similarity index 95% rename from utbot-spring-analyzer/src/main/kotlin/application/TestApplicationConfiguration.kt rename to utbot-spring-analyzer/src/main/kotlin/config/TestApplicationConfiguration.kt index cfb7732525..45fafe6955 100644 --- a/utbot-spring-analyzer/src/main/kotlin/application/TestApplicationConfiguration.kt +++ b/utbot-spring-analyzer/src/main/kotlin/config/TestApplicationConfiguration.kt @@ -1,4 +1,4 @@ -package application +package config import org.springframework.beans.factory.config.BeanFactoryPostProcessor import org.springframework.context.annotation.Bean From 1a22ef19eab3bd355801c1b411931118ff451224 Mon Sep 17 00:00:00 2001 From: Kirill Shishin Date: Tue, 14 Mar 2023 20:29:18 +0300 Subject: [PATCH 10/13] Added support for multiple configuration files of the same type. --- .../src/main/kotlin/ApplicationRunner.kt | 19 +++++---- .../kotlin/analyzers/PropertiesAnalyzer.kt | 22 ---------- .../analyzers/SpringApplicationAnalyzer.kt | 24 +++++++---- .../analyzers/XmlConfigurationAnalyzer.kt | 5 +-- .../configurators/PropertiesConfigurator.kt | 42 +++++++++++++++++++ .../configurators/XmlFilesConfigurator.kt | 28 +++++++++++++ .../main/kotlin/utils/ConfigurationManager.kt | 42 ++++++++++++++++--- .../src/main/kotlin/utils/FakeFileManager.kt | 31 ++++++++++++++ .../src/main/kotlin/utils/Paths.kt | 7 ++++ .../src/main/kotlin/utils/ResourceNames.kt | 6 --- .../resources/fake_application.properties | 0 .../src/main/resources/fake_application.xml | 6 --- 12 files changed, 172 insertions(+), 60 deletions(-) delete mode 100644 utbot-spring-analyzer/src/main/kotlin/analyzers/PropertiesAnalyzer.kt create mode 100644 utbot-spring-analyzer/src/main/kotlin/configurators/PropertiesConfigurator.kt create mode 100644 utbot-spring-analyzer/src/main/kotlin/configurators/XmlFilesConfigurator.kt create mode 100644 utbot-spring-analyzer/src/main/kotlin/utils/FakeFileManager.kt create mode 100644 utbot-spring-analyzer/src/main/kotlin/utils/Paths.kt delete mode 100644 utbot-spring-analyzer/src/main/kotlin/utils/ResourceNames.kt delete mode 100644 utbot-spring-analyzer/src/main/resources/fake_application.properties delete mode 100644 utbot-spring-analyzer/src/main/resources/fake_application.xml diff --git a/utbot-spring-analyzer/src/main/kotlin/ApplicationRunner.kt b/utbot-spring-analyzer/src/main/kotlin/ApplicationRunner.kt index 5b1f76425d..19c4cd93be 100644 --- a/utbot-spring-analyzer/src/main/kotlin/ApplicationRunner.kt +++ b/utbot-spring-analyzer/src/main/kotlin/ApplicationRunner.kt @@ -4,18 +4,23 @@ import analyzers.SpringApplicationAnalyzer fun main(args: Array) { /* - val arg0 = "D:/Projects/spring-starter-lesson-28/build/classes/java/main"; - val arg1 = "com.dmdev.spring.config.ApplicationConfiguration"; - val arg2 = "D:/Projects/spring-starter-lesson-28/src/main/resources/application.properties"; - val arg3 = "D:/Projects/spring-starter-lesson-28/src/main/resources/application.xml"; - val springApplicationAnalyzer = SpringApplicationAnalyzer(arg0, arg1, arg2, arg3) + arg2 contains .properties files, arg3 contains .xml files. + If there are several files, they are transmitted via ";". + If there are no files, then an empty string "" is passed + */ + + /* FOR EXAMPLE + val arg0 = "/Users/kirillshishin/IdeaProjects/spring-starter-lesson-28/build/classes/java/main" + val arg1 = "com.dmdev.spring.config.ApplicationConfiguration" + val arg2 = "/Users/kirillshishin/IdeaProjects/spring-starter-lesson-28/src/main/resources/application.properties;/Users/kirillshishin/IdeaProjects/spring-starter-lesson-28/src/main/resources/application-web.properties" + val arg3 = "/Users/kirillshishin/IdeaProjects/spring-starter-lesson-28/src/main/resources/application.xml;/Users/kirillshishin/IdeaProjects/spring-starter-lesson-28/src/main/resources/application2.xml" */ val springApplicationAnalyzer = SpringApplicationAnalyzer( applicationPath = args[0], configurationClassFqn = args[1], - propertyFilePath = args[2], - xmlConfigurationPath = args[3], + propertyFilesPaths = args[2].split(";"), + xmlConfigurationPaths = args[3].split(";"), ) springApplicationAnalyzer.analyze() diff --git a/utbot-spring-analyzer/src/main/kotlin/analyzers/PropertiesAnalyzer.kt b/utbot-spring-analyzer/src/main/kotlin/analyzers/PropertiesAnalyzer.kt deleted file mode 100644 index 32179429a0..0000000000 --- a/utbot-spring-analyzer/src/main/kotlin/analyzers/PropertiesAnalyzer.kt +++ /dev/null @@ -1,22 +0,0 @@ -package analyzers - -import java.io.BufferedReader -import java.io.FileReader -import java.io.IOException - -class PropertiesAnalyzer(private val propertiesFilePath: String) { - fun readProperties(): ArrayList { - val props = ArrayList() - - val reader = BufferedReader(FileReader(propertiesFilePath)) - var line = reader.readLine() - while (line != null) { - props.add(line) - line = reader.readLine() - } - - reader.close() - - return props - } -} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/analyzers/SpringApplicationAnalyzer.kt b/utbot-spring-analyzer/src/main/kotlin/analyzers/SpringApplicationAnalyzer.kt index 2345ffe69d..c1a2c0926a 100644 --- a/utbot-spring-analyzer/src/main/kotlin/analyzers/SpringApplicationAnalyzer.kt +++ b/utbot-spring-analyzer/src/main/kotlin/analyzers/SpringApplicationAnalyzer.kt @@ -1,5 +1,8 @@ package analyzers +import application.utils.FakeFileManager +import application.configurators.PropertiesConfigurator +import application.configurators.XmlFilesConfigurator import config.TestApplicationConfiguration import org.springframework.boot.builder.SpringApplicationBuilder import org.springframework.context.ApplicationContextException @@ -12,29 +15,30 @@ import java.nio.file.Path class SpringApplicationAnalyzer( private val applicationPath: String, private val configurationClassFqn: String, - private val propertyFilePath: String, - private val xmlConfigurationPath: String, + private val propertyFilesPaths: List, + private val xmlConfigurationPaths: List, ) { private val applicationUrl: URL get() = Path.of(applicationPath).toUri().toURL() fun analyze() { + val fakeFileManager = FakeFileManager(propertyFilesPaths + xmlConfigurationPaths) + fakeFileManager.createFakeFiles() + val classLoader: ClassLoader = URLClassLoader(arrayOf(applicationUrl)) val userConfigurationClass = classLoader.loadClass(configurationClassFqn) val configurationManager = ConfigurationManager(classLoader, userConfigurationClass) - val propertiesAnalyzer = PropertiesAnalyzer(propertyFilePath) - val xmlConfigurationAnalyzer = XmlConfigurationAnalyzer(xmlConfigurationPath) - - xmlConfigurationAnalyzer.fillFakeApplicationXml() + val propertiesConfigurator = PropertiesConfigurator(propertyFilesPaths, configurationManager) + val xmlFilesConfigurator = XmlFilesConfigurator(xmlConfigurationPaths, configurationManager, fakeFileManager) - configurationManager.patchPropertySourceAnnotation() - configurationManager.patchImportResourceAnnotation() + propertiesConfigurator.configure() + xmlFilesConfigurator.configure() val app = SpringApplicationBuilder(SpringApplicationAnalyzer::class.java) app.sources(TestApplicationConfiguration::class.java, userConfigurationClass) - for (prop in propertiesAnalyzer.readProperties()) { + for (prop in propertiesConfigurator.readProperties()) { app.properties(prop) } @@ -43,6 +47,8 @@ class SpringApplicationAnalyzer( app.run() } catch (e: ApplicationContextException) { println("Bean analysis finished successfully") + }finally { + fakeFileManager.deleteFakeFiles() } } } \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/analyzers/XmlConfigurationAnalyzer.kt b/utbot-spring-analyzer/src/main/kotlin/analyzers/XmlConfigurationAnalyzer.kt index b870d93a1e..6b2eb425dd 100644 --- a/utbot-spring-analyzer/src/main/kotlin/analyzers/XmlConfigurationAnalyzer.kt +++ b/utbot-spring-analyzer/src/main/kotlin/analyzers/XmlConfigurationAnalyzer.kt @@ -2,15 +2,12 @@ package analyzers import org.w3c.dom.Document import org.w3c.dom.Element -import utils.ResourceNames import javax.xml.parsers.DocumentBuilderFactory import javax.xml.transform.TransformerFactory import javax.xml.transform.dom.DOMSource import javax.xml.transform.stream.StreamResult -class XmlConfigurationAnalyzer(private val userXmlFilePath: String) { - private val fakeXmlFilePath = this.javaClass.classLoader.getResource(ResourceNames.fakeApplicationXmlFileName)?.path - ?: error("The path must exist") +class XmlConfigurationAnalyzer(private val userXmlFilePath: String, private val fakeXmlFilePath: String) { fun fillFakeApplicationXml() { val builder = DocumentBuilderFactory.newInstance().newDocumentBuilder() diff --git a/utbot-spring-analyzer/src/main/kotlin/configurators/PropertiesConfigurator.kt b/utbot-spring-analyzer/src/main/kotlin/configurators/PropertiesConfigurator.kt new file mode 100644 index 0000000000..545dd3d1bd --- /dev/null +++ b/utbot-spring-analyzer/src/main/kotlin/configurators/PropertiesConfigurator.kt @@ -0,0 +1,42 @@ +package application.configurators + +import utils.ConfigurationManager +import utils.Paths +import java.io.BufferedReader +import java.io.FileReader +import kotlin.io.path.Path + +class PropertiesConfigurator( + private val propertiesFilesPaths: List, + private val configurationManager: ConfigurationManager +) { + + fun configure(){ + configurationManager.clearPropertySourceAnnotation() + + for(propertiesFilePath in propertiesFilesPaths) { + if(propertiesFilePath == Paths.EMPTY_PATH)continue + + configurationManager.patchPropertySourceAnnotation(Path(propertiesFilePath).fileName) + } + } + + fun readProperties(): ArrayList { + val props = ArrayList() + + for(propertiesFilePath in propertiesFilesPaths) { + if(propertiesFilePath == Paths.EMPTY_PATH)continue + + val reader = BufferedReader(FileReader(propertiesFilePath)) + var line = reader.readLine() + while (line != null) { + props.add(line) + line = reader.readLine() + } + + reader.close() + } + + return props + } +} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/configurators/XmlFilesConfigurator.kt b/utbot-spring-analyzer/src/main/kotlin/configurators/XmlFilesConfigurator.kt new file mode 100644 index 0000000000..8ce2b3227f --- /dev/null +++ b/utbot-spring-analyzer/src/main/kotlin/configurators/XmlFilesConfigurator.kt @@ -0,0 +1,28 @@ +package application.configurators + +import analyzers.XmlConfigurationAnalyzer +import application.utils.FakeFileManager +import utils.ConfigurationManager +import utils.Paths +import kotlin.io.path.Path + +class XmlFilesConfigurator( + private val userXmlFilePaths: List, + private val configurationManager: ConfigurationManager, + private val fakeFileManager: FakeFileManager +) { + + fun configure() { + configurationManager.clearImportResourceAnnotation() + + for (userXmlFilePath in userXmlFilePaths) { + if(userXmlFilePath == Paths.EMPTY_PATH)continue + + val xmlConfigurationAnalyzer = + XmlConfigurationAnalyzer(userXmlFilePath, fakeFileManager.getFakeFilePath(userXmlFilePath)) + + xmlConfigurationAnalyzer.fillFakeApplicationXml() + configurationManager.patchImportResourceAnnotation(Path(userXmlFilePath).fileName) + } + } +} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/utils/ConfigurationManager.kt b/utbot-spring-analyzer/src/main/kotlin/utils/ConfigurationManager.kt index 1da73139e0..59fe449a97 100644 --- a/utbot-spring-analyzer/src/main/kotlin/utils/ConfigurationManager.kt +++ b/utbot-spring-analyzer/src/main/kotlin/utils/ConfigurationManager.kt @@ -3,21 +3,40 @@ package utils import org.springframework.context.annotation.ImportResource import org.springframework.context.annotation.PropertySource import java.lang.reflect.InvocationHandler +import java.nio.file.Path import java.util.Arrays import kotlin.reflect.KClass class ConfigurationManager(private val classLoader: ClassLoader, private val userConfigurationClass: Class<*>) { - fun patchPropertySourceAnnotation() = - patchAnnotation(PropertySource::class, String.format("classpath:%s", ResourceNames.fakePropertiesFileName)) + fun clearPropertySourceAnnotation(){ + patchAnnotation( + PropertySource::class, null + ) + } + + fun clearImportResourceAnnotation(){ + patchAnnotation( + ImportResource::class, null + ) + } - fun patchImportResourceAnnotation() = + fun patchPropertySourceAnnotation(userPropertiesFileName: Path) = + patchAnnotation( + PropertySource::class, String.format( + "classpath:%s", "fake_$userPropertiesFileName" + ) + ) + + fun patchImportResourceAnnotation(userXmlFilePath: Path) = patchAnnotation( ImportResource::class, - String.format("classpath:%s", ResourceNames.fakeApplicationXmlFileName) + String.format( + "classpath:%s", "fake_$userXmlFilePath" + ) ) - private fun patchAnnotation(annotationClass: KClass<*>, newValue: String) { + private fun patchAnnotation(annotationClass: KClass<*>, newValue: String?) { val proxyClass = classLoader.loadClass("java.lang.reflect.Proxy") val hField = proxyClass.getDeclaredField("h") hField.isAccessible = true @@ -37,7 +56,18 @@ class ConfigurationManager(private val classLoader: ClassLoader, private val use memberValuesField.isAccessible = true val memberValues = memberValuesField[annotationInvocationHandler] as MutableMap - memberValues["value"] = newValue + addNewValue(memberValues, newValue) + } + } + + private fun addNewValue(memberValues: MutableMap, newValue: String?){ + if(newValue == null){ + memberValues["value"] = Array(0){""} + } + else { + val list: MutableList = (memberValues["value"] as Array).toMutableList() + list.add(newValue) + memberValues["value"] = list.toTypedArray() } } } \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/utils/FakeFileManager.kt b/utbot-spring-analyzer/src/main/kotlin/utils/FakeFileManager.kt new file mode 100644 index 0000000000..8934c12bb5 --- /dev/null +++ b/utbot-spring-analyzer/src/main/kotlin/utils/FakeFileManager.kt @@ -0,0 +1,31 @@ +package application.utils + +import utils.Paths +import java.io.File +import kotlin.io.path.Path + +class FakeFileManager(private val fakeFilesList: List) { + + private val buildResourcesPath = + Path(this.javaClass.classLoader.getResource(Paths.GAG_FILE)!!.path).parent.toString() + + fun createFakeFiles() { + for (fileName in fakeFilesList) { + if(fileName == Paths.EMPTY_FILENAME)continue + val fakeXmlFileAbsolutePath = getFakeFilePath(fileName) + File(fakeXmlFileAbsolutePath).createNewFile() + } + } + + fun deleteFakeFiles() { + for (fileName in fakeFilesList) { + if(fileName == Paths.EMPTY_FILENAME)continue + val fakeXmlFileAbsolutePath = getFakeFilePath(fileName) + File(fakeXmlFileAbsolutePath).delete() + } + } + + fun getFakeFilePath(fileName: String): String { + return Path(buildResourcesPath, "fake_${Path(fileName).fileName}").toString() + } +} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/utils/Paths.kt b/utbot-spring-analyzer/src/main/kotlin/utils/Paths.kt new file mode 100644 index 0000000000..2a00d2db93 --- /dev/null +++ b/utbot-spring-analyzer/src/main/kotlin/utils/Paths.kt @@ -0,0 +1,7 @@ +package utils + +object Paths { + const val GAG_FILE = "gag_file.txt" + const val EMPTY_PATH = "" + const val EMPTY_FILENAME = "" +} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/utils/ResourceNames.kt b/utbot-spring-analyzer/src/main/kotlin/utils/ResourceNames.kt deleted file mode 100644 index e6bc27b544..0000000000 --- a/utbot-spring-analyzer/src/main/kotlin/utils/ResourceNames.kt +++ /dev/null @@ -1,6 +0,0 @@ -package utils - -object ResourceNames { - const val fakePropertiesFileName = "fake_application.properties" - const val fakeApplicationXmlFileName = "fake_application.xml" -} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/resources/fake_application.properties b/utbot-spring-analyzer/src/main/resources/fake_application.properties deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/utbot-spring-analyzer/src/main/resources/fake_application.xml b/utbot-spring-analyzer/src/main/resources/fake_application.xml deleted file mode 100644 index 142def25af..0000000000 --- a/utbot-spring-analyzer/src/main/resources/fake_application.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file From 8c93dba68f6864ea519b31472e66a222afa336be Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Wed, 15 Mar 2023 15:29:42 +0300 Subject: [PATCH 11/13] Some pre-merge refactoring --- .../src/main/kotlin/ApplicationRunner.kt | 20 +++++++---- .../analyzers/SpringApplicationAnalyzer.kt | 2 +- .../configurators/PropertiesConfigurator.kt | 16 ++++----- .../configurators/XmlFilesConfigurator.kt | 7 ++-- .../UtBotBeanFactoryPostProcessor.kt | 35 +++++++++++++------ .../main/kotlin/utils/ConfigurationManager.kt | 25 +++---------- .../src/main/kotlin/utils/FakeFileManager.kt | 33 +++++++++-------- .../src/main/kotlin/utils/Paths.kt | 7 ---- .../src/main/kotlin/utils/PathsUtils.kt | 21 +++++++++++ .../src/main/resources/resourses_marker.txt | 1 + 10 files changed, 93 insertions(+), 74 deletions(-) delete mode 100644 utbot-spring-analyzer/src/main/kotlin/utils/Paths.kt create mode 100644 utbot-spring-analyzer/src/main/kotlin/utils/PathsUtils.kt create mode 100644 utbot-spring-analyzer/src/main/resources/resourses_marker.txt diff --git a/utbot-spring-analyzer/src/main/kotlin/ApplicationRunner.kt b/utbot-spring-analyzer/src/main/kotlin/ApplicationRunner.kt index 19c4cd93be..bf25d6b979 100644 --- a/utbot-spring-analyzer/src/main/kotlin/ApplicationRunner.kt +++ b/utbot-spring-analyzer/src/main/kotlin/ApplicationRunner.kt @@ -1,13 +1,19 @@ package application import analyzers.SpringApplicationAnalyzer +import utils.PathsUtils +/** + * To run this app, arguments must be passed in the following way: + * args[0] - classpath of current project + * args[1] - fully qualified name of configuration class + * args[2] - `.properties` file paths, separated via `;`, empty string if no files exist + * args[3] - `.xml` configuration file paths + * + * Several items in one arg are separated via `;`. + * If there are no files, empty string should be passed. + */ fun main(args: Array) { - /* - arg2 contains .properties files, arg3 contains .xml files. - If there are several files, they are transmitted via ";". - If there are no files, then an empty string "" is passed - */ /* FOR EXAMPLE val arg0 = "/Users/kirillshishin/IdeaProjects/spring-starter-lesson-28/build/classes/java/main" @@ -19,8 +25,8 @@ fun main(args: Array) { val springApplicationAnalyzer = SpringApplicationAnalyzer( applicationPath = args[0], configurationClassFqn = args[1], - propertyFilesPaths = args[2].split(";"), - xmlConfigurationPaths = args[3].split(";"), + propertyFilesPaths = args[2].split(";").filter { it != PathsUtils.EMPTY_PATH }, + xmlConfigurationPaths = args[3].split(";").filter { it != PathsUtils.EMPTY_PATH }, ) springApplicationAnalyzer.analyze() diff --git a/utbot-spring-analyzer/src/main/kotlin/analyzers/SpringApplicationAnalyzer.kt b/utbot-spring-analyzer/src/main/kotlin/analyzers/SpringApplicationAnalyzer.kt index c1a2c0926a..6277a24116 100644 --- a/utbot-spring-analyzer/src/main/kotlin/analyzers/SpringApplicationAnalyzer.kt +++ b/utbot-spring-analyzer/src/main/kotlin/analyzers/SpringApplicationAnalyzer.kt @@ -31,7 +31,7 @@ class SpringApplicationAnalyzer( val configurationManager = ConfigurationManager(classLoader, userConfigurationClass) val propertiesConfigurator = PropertiesConfigurator(propertyFilesPaths, configurationManager) - val xmlFilesConfigurator = XmlFilesConfigurator(xmlConfigurationPaths, configurationManager, fakeFileManager) + val xmlFilesConfigurator = XmlFilesConfigurator(xmlConfigurationPaths, configurationManager) propertiesConfigurator.configure() xmlFilesConfigurator.configure() diff --git a/utbot-spring-analyzer/src/main/kotlin/configurators/PropertiesConfigurator.kt b/utbot-spring-analyzer/src/main/kotlin/configurators/PropertiesConfigurator.kt index 545dd3d1bd..f2abe3eef1 100644 --- a/utbot-spring-analyzer/src/main/kotlin/configurators/PropertiesConfigurator.kt +++ b/utbot-spring-analyzer/src/main/kotlin/configurators/PropertiesConfigurator.kt @@ -1,7 +1,7 @@ package application.configurators import utils.ConfigurationManager -import utils.Paths +import utils.PathsUtils import java.io.BufferedReader import java.io.FileReader import kotlin.io.path.Path @@ -11,21 +11,19 @@ class PropertiesConfigurator( private val configurationManager: ConfigurationManager ) { - fun configure(){ + fun configure() { configurationManager.clearPropertySourceAnnotation() - for(propertiesFilePath in propertiesFilesPaths) { - if(propertiesFilePath == Paths.EMPTY_PATH)continue - - configurationManager.patchPropertySourceAnnotation(Path(propertiesFilePath).fileName) - } + propertiesFilesPaths + .map { Path(it).fileName } + .forEach { fileName -> configurationManager.patchPropertySourceAnnotation(fileName) } } fun readProperties(): ArrayList { val props = ArrayList() - for(propertiesFilePath in propertiesFilesPaths) { - if(propertiesFilePath == Paths.EMPTY_PATH)continue + for (propertiesFilePath in propertiesFilesPaths) { + if (propertiesFilePath == PathsUtils.EMPTY_PATH) continue val reader = BufferedReader(FileReader(propertiesFilePath)) var line = reader.readLine() diff --git a/utbot-spring-analyzer/src/main/kotlin/configurators/XmlFilesConfigurator.kt b/utbot-spring-analyzer/src/main/kotlin/configurators/XmlFilesConfigurator.kt index 8ce2b3227f..677b1aee51 100644 --- a/utbot-spring-analyzer/src/main/kotlin/configurators/XmlFilesConfigurator.kt +++ b/utbot-spring-analyzer/src/main/kotlin/configurators/XmlFilesConfigurator.kt @@ -3,23 +3,22 @@ package application.configurators import analyzers.XmlConfigurationAnalyzer import application.utils.FakeFileManager import utils.ConfigurationManager -import utils.Paths +import utils.PathsUtils import kotlin.io.path.Path class XmlFilesConfigurator( private val userXmlFilePaths: List, private val configurationManager: ConfigurationManager, - private val fakeFileManager: FakeFileManager ) { fun configure() { configurationManager.clearImportResourceAnnotation() for (userXmlFilePath in userXmlFilePaths) { - if(userXmlFilePath == Paths.EMPTY_PATH)continue + if(userXmlFilePath == PathsUtils.EMPTY_PATH)continue val xmlConfigurationAnalyzer = - XmlConfigurationAnalyzer(userXmlFilePath, fakeFileManager.getFakeFilePath(userXmlFilePath)) + XmlConfigurationAnalyzer(userXmlFilePath, PathsUtils.createFakeFilePath(userXmlFilePath)) xmlConfigurationAnalyzer.fillFakeApplicationXml() configurationManager.patchImportResourceAnnotation(Path(userXmlFilePath).fileName) diff --git a/utbot-spring-analyzer/src/main/kotlin/post_processors/UtBotBeanFactoryPostProcessor.kt b/utbot-spring-analyzer/src/main/kotlin/post_processors/UtBotBeanFactoryPostProcessor.kt index 1866fddcf9..c0c15023e3 100644 --- a/utbot-spring-analyzer/src/main/kotlin/post_processors/UtBotBeanFactoryPostProcessor.kt +++ b/utbot-spring-analyzer/src/main/kotlin/post_processors/UtBotBeanFactoryPostProcessor.kt @@ -1,6 +1,5 @@ package post_processors -import org.springframework.beans.BeansException import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition import org.springframework.beans.factory.config.BeanFactoryPostProcessor import org.springframework.beans.factory.config.ConfigurableListableBeanFactory @@ -13,9 +12,26 @@ import java.util.Arrays class UtBotBeanFactoryPostProcessor : BeanFactoryPostProcessor, PriorityOrdered { + /** + * Sets the priority of post processor to highest to avoid side effects from others. + */ + override fun getOrder(): Int = PriorityOrdered.HIGHEST_PRECEDENCE + override fun postProcessBeanFactory(beanFactory: ConfigurableListableBeanFactory) { println("Started post-processing bean factory in UtBot") + val beanClassNames = findBeanClassNames(beanFactory) + //TODO: will be replaced with more appropriate IPC approach. + writeToFile(beanClassNames) + + // After desired post-processing is completed we destroy bean definitions + // to avoid further possible actions with beans that may be unsafe. + destroyBeanDefinitions(beanFactory) + + println("Finished post-processing bean factory in UtBot") + } + + private fun findBeanClassNames(beanFactory: ConfigurableListableBeanFactory): ArrayList { val beanClassNames = ArrayList() for (beanDefinitionName in beanFactory.beanDefinitionNames) { val beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName) @@ -33,12 +49,15 @@ class UtBotBeanFactoryPostProcessor : BeanFactoryPostProcessor, PriorityOrdered className?.let { beanClassNames.add(it) } } } + + return beanClassNames + } + + private fun destroyBeanDefinitions(beanFactory: ConfigurableListableBeanFactory) { for (beanDefinitionName in beanFactory.beanDefinitionNames) { val beanRegistry = beanFactory as BeanDefinitionRegistry beanRegistry.removeBeanDefinition(beanDefinitionName) } - - writeToFile(beanClassNames) } private fun writeToFile(beanClassNames: ArrayList) { @@ -58,14 +77,10 @@ class UtBotBeanFactoryPostProcessor : BeanFactoryPostProcessor, PriorityOrdered fileWriter.flush() fileWriter.close() + + println("Storing bean information completed successfully") } catch (e: Throwable) { - println("Storing bean information failed") - } finally { - println("Finished post-processing bean factory in UtBot") + println("Storing bean information failed with exception $e") } } - - override fun getOrder(): Int { - return PriorityOrdered.HIGHEST_PRECEDENCE - } } \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/utils/ConfigurationManager.kt b/utbot-spring-analyzer/src/main/kotlin/utils/ConfigurationManager.kt index 59fe449a97..daff28f9b3 100644 --- a/utbot-spring-analyzer/src/main/kotlin/utils/ConfigurationManager.kt +++ b/utbot-spring-analyzer/src/main/kotlin/utils/ConfigurationManager.kt @@ -9,32 +9,15 @@ import kotlin.reflect.KClass class ConfigurationManager(private val classLoader: ClassLoader, private val userConfigurationClass: Class<*>) { - fun clearPropertySourceAnnotation(){ - patchAnnotation( - PropertySource::class, null - ) - } + fun clearPropertySourceAnnotation() = patchAnnotation(PropertySource::class, null) - fun clearImportResourceAnnotation(){ - patchAnnotation( - ImportResource::class, null - ) - } + fun clearImportResourceAnnotation() = patchAnnotation(ImportResource::class, null) fun patchPropertySourceAnnotation(userPropertiesFileName: Path) = - patchAnnotation( - PropertySource::class, String.format( - "classpath:%s", "fake_$userPropertiesFileName" - ) - ) + patchAnnotation(PropertySource::class, String.format("classpath:%s", "fake_$userPropertiesFileName")) fun patchImportResourceAnnotation(userXmlFilePath: Path) = - patchAnnotation( - ImportResource::class, - String.format( - "classpath:%s", "fake_$userXmlFilePath" - ) - ) + patchAnnotation(ImportResource::class, String.format("classpath:%s", "fake_$userXmlFilePath")) private fun patchAnnotation(annotationClass: KClass<*>, newValue: String?) { val proxyClass = classLoader.loadClass("java.lang.reflect.Proxy") diff --git a/utbot-spring-analyzer/src/main/kotlin/utils/FakeFileManager.kt b/utbot-spring-analyzer/src/main/kotlin/utils/FakeFileManager.kt index 8934c12bb5..835262a072 100644 --- a/utbot-spring-analyzer/src/main/kotlin/utils/FakeFileManager.kt +++ b/utbot-spring-analyzer/src/main/kotlin/utils/FakeFileManager.kt @@ -1,31 +1,34 @@ package application.utils -import utils.Paths +import utils.PathsUtils import java.io.File -import kotlin.io.path.Path +import java.io.IOException class FakeFileManager(private val fakeFilesList: List) { - private val buildResourcesPath = - Path(this.javaClass.classLoader.getResource(Paths.GAG_FILE)!!.path).parent.toString() - fun createFakeFiles() { for (fileName in fakeFilesList) { - if(fileName == Paths.EMPTY_FILENAME)continue - val fakeXmlFileAbsolutePath = getFakeFilePath(fileName) - File(fakeXmlFileAbsolutePath).createNewFile() + val fakeXmlFileAbsolutePath = PathsUtils.createFakeFilePath(fileName) + + try { + File(fakeXmlFileAbsolutePath).createNewFile() + } catch (e: IOException) { + println("Fake xml file creation failed with exception $e") + } + } } fun deleteFakeFiles() { for (fileName in fakeFilesList) { - if(fileName == Paths.EMPTY_FILENAME)continue - val fakeXmlFileAbsolutePath = getFakeFilePath(fileName) - File(fakeXmlFileAbsolutePath).delete() - } - } + val fakeXmlFileAbsolutePath = PathsUtils.createFakeFilePath(fileName) + + try { + File(fakeXmlFileAbsolutePath).delete() + } catch (e: IOException) { + println("Fake xml file deletion failed with exception $e") + } - fun getFakeFilePath(fileName: String): String { - return Path(buildResourcesPath, "fake_${Path(fileName).fileName}").toString() + } } } \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/utils/Paths.kt b/utbot-spring-analyzer/src/main/kotlin/utils/Paths.kt deleted file mode 100644 index 2a00d2db93..0000000000 --- a/utbot-spring-analyzer/src/main/kotlin/utils/Paths.kt +++ /dev/null @@ -1,7 +0,0 @@ -package utils - -object Paths { - const val GAG_FILE = "gag_file.txt" - const val EMPTY_PATH = "" - const val EMPTY_FILENAME = "" -} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/kotlin/utils/PathsUtils.kt b/utbot-spring-analyzer/src/main/kotlin/utils/PathsUtils.kt new file mode 100644 index 0000000000..e2b3b88830 --- /dev/null +++ b/utbot-spring-analyzer/src/main/kotlin/utils/PathsUtils.kt @@ -0,0 +1,21 @@ +package utils + +import kotlin.io.path.Path + +object PathsUtils { + const val EMPTY_PATH = "" + + fun createFakeFilePath(fileName: String): String = + Path(buildResourcesPath, "fake_${Path(fileName).fileName}").toString() + + //TODO: it is better to do it without marker files + private val buildResourcesPath: String + get() { + val resourcesMarker = + this.javaClass.classLoader.getResource("resources_marker.txt") + ?: error("Resources marker file is not found") + + return Path(resourcesMarker.path).parent.toString() + } + +} \ No newline at end of file diff --git a/utbot-spring-analyzer/src/main/resources/resourses_marker.txt b/utbot-spring-analyzer/src/main/resources/resourses_marker.txt new file mode 100644 index 0000000000..4c7bb149cb --- /dev/null +++ b/utbot-spring-analyzer/src/main/resources/resourses_marker.txt @@ -0,0 +1 @@ +DO NOT DELETE THIS FILE!!! \ No newline at end of file From d661c75075ba7cf3726464bc5b7a1459e1a7946c Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Wed, 15 Mar 2023 15:55:46 +0300 Subject: [PATCH 12/13] Update target compatibility --- utbot-spring-analyzer/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utbot-spring-analyzer/build.gradle.kts b/utbot-spring-analyzer/build.gradle.kts index 11adc7e391..ee9048e4a8 100644 --- a/utbot-spring-analyzer/build.gradle.kts +++ b/utbot-spring-analyzer/build.gradle.kts @@ -6,7 +6,7 @@ plugins { java { sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_17 } dependencies { From 887ffabd8c50a4e2d222ae7cc70822c062136f6d Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Wed, 15 Mar 2023 23:24:16 +0300 Subject: [PATCH 13/13] Misprint fixed --- .../src/main/resources/resour\321\201es_marker.txt" | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename utbot-spring-analyzer/src/main/resources/resourses_marker.txt => "utbot-spring-analyzer/src/main/resources/resour\321\201es_marker.txt" (100%) diff --git a/utbot-spring-analyzer/src/main/resources/resourses_marker.txt "b/utbot-spring-analyzer/src/main/resources/resour\321\201es_marker.txt" similarity index 100% rename from utbot-spring-analyzer/src/main/resources/resourses_marker.txt rename to "utbot-spring-analyzer/src/main/resources/resour\321\201es_marker.txt"