Search code examples
springjpaeclipselinkspring-boot

Spring boot - Eclipelink scanning orm.xml


I'm converting an existing application to spring boot, when I run the application using the created jar I get an exception when it scans and finds an orm.xml inside my entities jar (included as a dependency).

org.eclipse.persistence.exceptions.PersistenceUnitLoadingException:
Exception Description: An exception was thrown while searching for persistence archives with ClassLoader: org.springframework.boot.loader.LaunchedURLClassLoader@38cdedfd
Internal Exception: javax.persistence.PersistenceException: Exception [EclipseLink-28018] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Predeployment of PersistenceUnit [UAccessPersistenceUnit] failed.
Internal Exception: java.lang.RuntimeException: java.io.IOException: Unable to open root Jar file 'jar:file:everything-jar.jar'
        at org.eclipse.persistence.exceptions.PersistenceUnitLoadingException.exceptionSearchingForPersistenceResources(PersistenceUnitLoadingException.java:127)
                at org.eclipse.persistence.jpa.PersistenceProvider.createEntityManagerFactoryImpl(PersistenceProvider.java:107)
                        at org.eclipse.persistence.jpa.PersistenceProvider.createEntityManagerFactory(PersistenceProvider.java:177)
                                at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:79)


Caused by: javax.persistence.PersistenceException: Exception [EclipseLink-28018] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Predeployment of PersistenceUnit [UAccessPersistenceUnit] failed.
Internal Exception: java.lang.RuntimeException: java.io.IOException: Unable to open root Jar file 'jar:file:everything-jar.jar'
        at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.createPredeployFailedPersistenceException(EntityManagerSetupImpl.java:1954)
                at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.predeploy(EntityManagerSetupImpl.java:1945)
                        at org.eclipse.persistence.internal.jpa.deployment.JPAInitializer.callPredeploy(JPAInitializer.java:98)
                                at org.eclipse.persistence.jpa.PersistenceProvider.createEntityManagerFactoryImpl(PersistenceProvider.java:96)
                                        ... 46 common frames omitted
                                        Caused by: org.eclipse.persistence.exceptions.EntityManagerSetupException:
                                        Exception Description: Predeployment of PersistenceUnit [UAccessPersistenceUnit] failed.
                                        Internal Exception: java.lang.RuntimeException: java.io.IOException: Unable to open root Jar file 'jar:file:everything-jar.jar'
                                                at org.eclipse.persistence.exceptions.EntityManagerSetupException.predeployFailed(EntityManagerSetupException.java:230)
                                                        ... 50 common frames omitted

This isn't using spring data, i'm building the datasources from a external properties file at the moment. It's the scanning that seems to cause issues.

This is spring boot 1.1.5-RELEASE with eclipselink 2.5.1

everything-jar.jar is a spring boot packed jar. With my compiles classes, the spring boot loader and the entities packaged in a jar. I've marked the dependency to be unpacked and it works, but it's not ideal.

This problem keeps coming up, now if I try to load persistence.xml from another jar it throws this same exception.


Solution

  • After a lot of investigation it seems there are two parts to the problem here.

    1. The spring boot loader supports urls that look something like this : jar:file:!/!/

    2. Eclipselink generates urls for embedded orm.xml files like so jar:jar:file:!/!/

    I've got a fix in spring boot to make it better at opening the eclipselink URLs, but for an immediate fix we need to create a custom ArchiveFactoryImpl and override the createArchive method to return a custom JarInputStreamURLArchive when the protocol is jar.

    In this custom class override the get entry as URL to check for a url with a protocol of jar that starts with jar and create a new URL from the file of the original (this cuts off the protocol part from the original url giving us jar:url instead of jar:url:url

    public URL getEntryAsURL(String entryPath) throws IOException {
                URL entryUrl = super.getEntryAsURL(entryPath);
    
                if(entryUrl == null) {
                    return null;
                }
    
                if("jar".equals(entryUrl.getProtocol()) && entryUrl.getFile().startsWith("jar:")) {
                    //cut off the jar portions
                    String file = entryUrl.getFile();
                    return new URL(file);
                } else {
                    return entryUrl;
                }
            }