manifold-systems / manifold

Manifold is a Java compiler plugin, its features include Metaprogramming, Properties, Extension Methods, Operator Overloading, Templates, a Preprocessor, and more.
http://manifold.systems/
Apache License 2.0
2.41k stars 125 forks source link

Spring and Spring boot support #22

Closed arturhg closed 6 years ago

arturhg commented 6 years ago

Is it possible to use Manifold with Spring and Spring Boot projects? I tried Manifold with Spring Boot 1.x and it crashed with different exceptions each time. Probably should try again and post trace here. Thank you.

rsmckinney commented 6 years ago

Hi arturhg.

Thanks for the report!

Quick answer. YES. It is possible to use Manifold with Spring.

I've generated a demo Spring app using spring initializr, added Manifold dependency, and a simple use-case. I can reproduce an exception (attached to this post) with a call to SpringApplication.run().

Update:

Good news! After further inspection the exception from Spring is actually just a scary warning logged to the console, it does not interrupt normal execution, therefore nothing to be concerned about. In any case, you can use the workaround documented in this post to avoid the warning.

Based on the stack trace it appears Spring scans the classpath of the application's URLClassLoader and assumes all the URLs are files. Unfortunately, they may not all be files because Manifold adds a special URL to the class loader with the "manifoldclass" protocol. In my view Spring should simply skip URLs that are not files instead of allowing the exception to fall through -- there is no harm in skipping a URL it can't handle. Perhaps I'll add a pull request to fix this.

Fortunately, you can avoid this problem in a number of different ways, depending on your needs. The easiest fix is to simply add the @NoBootstrap annotation to the main class e.g.,

@SpringBootApplication
@NoBootstrap
public class DemoApplication {

This tell Manifold not to generate a class initializer to bootstrap Manifold at runtime. Instead another class that has the initializer will do it. However, if your main class requires runtime features you'll have to bootstrap manually before the runtime is needed. Doing this involves adding a simple call wherever you need it:

Bootstrap.init();

Features that require Manifold runtime include:

Let me know if this helps.

There are other ways to work around the exception. I'll document them and push the Spring sample app to github for future reference.

Let me know if you have additional questions or issues.

Thanks again for the report!

Scott

Exception: (note this exception is merely logged, it does not stop normal execution of your application)

java.io.FileNotFoundException: URL [manifoldclass://414493378/com/example/demo/] cannot be resolved to absolute file path because it does not reside in the file system: manifoldclass://414493378/com/example/demo/
    at org.springframework.util.ResourceUtils.getFile(ResourceUtils.java:217) ~[spring-core-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.core.io.AbstractFileResolvingResource.getFile(AbstractFileResolvingResource.java:131) ~[spring-core-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.core.io.UrlResource.getFile(UrlResource.java:225) ~[spring-core-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.core.io.support.PathMatchingResourcePatternResolver.doFindPathMatchingFileResources(PathMatchingResourcePatternResolver.java:697) [spring-core-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.core.io.support.PathMatchingResourcePatternResolver.findPathMatchingResources(PathMatchingResourcePatternResolver.java:510) [spring-core-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.core.io.support.PathMatchingResourcePatternResolver.getResources(PathMatchingResourcePatternResolver.java:282) [spring-core-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.getResources(AbstractApplicationContext.java:1307) [spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.support.GenericApplicationContext.getResources(GenericApplicationContext.java:233) [spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.scanCandidateComponents(ClassPathScanningCandidateComponentProvider.java:421) [spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents(ClassPathScanningCandidateComponentProvider.java:316) [spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:275) [spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:132) [spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:288) [spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:245) [spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:202) [spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:170) [spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:316) [spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:233) [spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:271) [spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:91) [spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:692) [spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:530) [spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) [spring-boot-2.0.6.RELEASE.jar:2.0.6.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:386) [spring-boot-2.0.6.RELEASE.jar:2.0.6.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-2.0.6.RELEASE.jar:2.0.6.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1242) [spring-boot-2.0.6.RELEASE.jar:2.0.6.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1230) [spring-boot-2.0.6.RELEASE.jar:2.0.6.RELEASE]
    at com.example.demo.DemoApplication.main(DemoApplication.java:11) [classes/:na]
arturhg commented 6 years ago

Amazing answer, rsmckinney! Will try very soon.

rsmckinney commented 6 years ago

Update

I logged https://jira.spring.io/browse/SPR-17417, which is now fixed :)

arturhg commented 6 years ago

Thank you so much, rsmckinney! I started trying and will share the updates.

arturhg commented 6 years ago

It works! Thanks a lot.

marksto commented 5 years ago

Hi @rsmckinney!

You promised once...

I'll document them and push the Spring sample app to github for future reference.

... So where can one find this aforementioned template? =)

rsmckinney commented 5 years ago

Hi @marksto. Lol, that was a while ago. As I recall after the bug in Spring was fixed to eliminate the warning message I realized there wasn't anything special to do -- you can simply add Manifold dependencies to existing Spring-based projects. Here is the POM file from the app used:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>systems.manifold</groupId>
            <artifactId>manifold-all</artifactId>
            <version>0.59-alpha</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <compilerArgs>

                        <!--Add the Manifold plugin, with string templates and checked exception suppression enabled-->
                        <arg>-Xplugin:Manifold strings exceptions</arg>

                    </compilerArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Let me know if you're having trouble using Manifold with a Spring app.

Cheers.

keshwanramlu commented 3 months ago

I am building a jar from spring boot application that uses manifold for dynamic json 2 java. and get the following error only for jar execution but runs fine on IntelliJ. java.lang.NullPointerException: Cannot invoke "manifold.rt.api.util.Pair.getFirst()" because "classSymbol" is null at manifold.ext.DynamicProxyFactory.hasCallHandlerFromExtension(DynamicProxyFactory.java:117) ~[manifold-ext-2024.1.25.jar!/:2024.1.25] at manifold.ext.DynamicProxyFactory.hasCallHandlerMethod(DynamicProxyFactory.java:102) ~[manifold-ext-2024.1.25.jar!/:2024.1.25] at manifold.ext.DynamicProxyFactory.makeProxyFactory(DynamicProxyFactory.java:52) ~[manifold-ext-2024.1.25.jar!/:2024.1.25] at manifold.ext.rt.RuntimeMethods.createProxy(RuntimeMethods.java:415) ~[manifold-ext-rt-2024.1.25.jar!/:2024.1.25] at manifold.ext.rt.RuntimeMethods.createNewProxy(RuntimeMethods.java:387) ~[manifold-ext-rt-2024.1.25.jar!/:2024.1.25] at manifold.ext.rt.RuntimeMethods.constructProxy(RuntimeMethods.java:63) ~[manifold-ext-rt-2024.1.25.jar!/:2024.1.25] at org.sampler.algorithm.Algorithm.getPillarToJourneyMapping(Algorithm.java:169) ~[!/:0.0.1-SNAPSHOT] at org.sampler.algorithm.Algorithm.getPathwayForPersona(Algorithm.java:63) ~[!/:0.0.1-SNAPSHOT] at org.sampler.algorithm.Application.run(Application.java:185) ~[!/:0.0.1-SNAPSHOT] at org.springframework.boot.SpringApplication.lambda$callRunner$5(SpringApplication.java:790) ~[spring-boot-3.3.1.jar!/:3.3.1] at org.springframework.util.function.ThrowingConsumer$1.acceptWithException(ThrowingConsumer.java:83) ~[spring-core-6.1.10.jar!/:6.1.10] at org.springframework.util.function.ThrowingConsumer.accept(ThrowingConsumer.java:60) ~[spring-core-6.1.10.jar!/:6.1.10] at org.springframework.util.function.ThrowingConsumer$1.accept(ThrowingConsumer.java:88) ~[spring-core-6.1.10.jar!/:6.1.10] at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) ~[spring-boot-3.3.1.jar!/:3.3.1] at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:789) ~[spring-boot-3.3.1.jar!/:3.3.1] at org.springframework.boot.SpringApplication.lambda$callRunners$3(SpringApplication.java:774) ~[spring-boot-3.3.1.jar!/:3.3.1] at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) ~[na:na] at java.base/java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357) ~[na:na] at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510) ~[na:na] at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na] at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) ~[na:na] at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) ~[na:na] at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na] at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) ~[na:na] at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:774) ~[spring-boot-3.3.1.jar!/:3.3.1] at org.springframework.boot.SpringApplication.run(SpringApplication.java:342) ~[spring-boot-3.3.1.jar!/:3.3.1] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.3.1.jar!/:3.3.1] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.3.1.jar!/:3.3.1] at org.sampler.algorithm.Application.main(Application.java:43) ~[!/:0.0.1-SNAPSHOT] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na] at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:91) ~[algorithm-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT] at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:53) ~[algorithm-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT] at org.springframework.boot.loader.launch.JarLauncher.main(JarLauncher.java:58) ~[algorithm-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]