From 023bb41911f5589de56779d9d18ed7b12da00c9d Mon Sep 17 00:00:00 2001 From: Aihua Xu Date: Sun, 4 Aug 2024 17:43:32 -0700 Subject: [PATCH] Support external config file for eclipselink Due to the limitation of eclipselink, the config file has to be a resource inside a jar so a config file like /tmp/persistence.xml can't be supported directly. To workaround the issue, 1. create a jar file from persistence.xml through 'jar /tmp/cf conf.jar ./persistence.xml' 3. configure eclipselink in polaris-server.yml metaStoreManager: type: eclipse-link persistence-unit: polaris conf-file: /tmp/conf.jar!/persistence.xml --- .../persistence/eclipselink/build.gradle | 8 ++ ...olarisEclipseLinkMetaStoreSessionImpl.java | 85 ++++++++++++------ .../src/test/data/eclipselink_conf.jar | Bin 0 -> 1322 bytes .../PolarisEclipseLinkMetaStoreTest.java | 36 ++++++++ polaris-server.yml | 3 +- ...s.core.persistence.MetaStoreManagerFactory | 2 - .../test/resources/META-INF/persistence.xml | 2 +- .../polaris-server-integrationtest.yml | 2 + 8 files changed, 108 insertions(+), 30 deletions(-) create mode 100644 extension/persistence/eclipselink/src/test/data/eclipselink_conf.jar diff --git a/extension/persistence/eclipselink/build.gradle b/extension/persistence/eclipselink/build.gradle index 91fb0c9c7..27b0bee4c 100644 --- a/extension/persistence/eclipselink/build.gradle +++ b/extension/persistence/eclipselink/build.gradle @@ -24,4 +24,12 @@ dependencies { testImplementation(libs.h2) testImplementation(testFixtures(project(":polaris-core"))) + + sourceSets { + test { + resources { + srcDir 'src/test/data' + } + } + } } diff --git a/extension/persistence/eclipselink/src/main/java/io/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreSessionImpl.java b/extension/persistence/eclipselink/src/main/java/io/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreSessionImpl.java index e1f7bec83..a49b6071f 100644 --- a/extension/persistence/eclipselink/src/main/java/io/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreSessionImpl.java +++ b/extension/persistence/eclipselink/src/main/java/io/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreSessionImpl.java @@ -19,7 +19,6 @@ import static org.eclipse.persistence.config.PersistenceUnitProperties.JDBC_URL; import com.google.common.base.Predicates; -import com.google.common.collect.Maps; import io.polaris.core.PolarisCallContext; import io.polaris.core.context.RealmContext; import io.polaris.core.entity.PolarisBaseEntity; @@ -47,7 +46,11 @@ import jakarta.persistence.EntityTransaction; import jakarta.persistence.OptimisticLockException; import jakarta.persistence.Persistence; +import java.io.File; +import java.io.IOException; import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -67,6 +70,7 @@ import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; /** * EclipseLink implementation of a Polaris metadata store supporting persisting and retrieving all @@ -80,7 +84,6 @@ public class PolarisEclipseLinkMetaStoreSessionImpl implements PolarisMetaStoreS private final ThreadLocal localSession = new ThreadLocal<>(); private final PolarisEclipseLinkStore store; private final PolarisStorageIntegrationProvider storageIntegrationProvider; - private static volatile Map properties; /** * Create a meta store session against provided realm. Each realm has its own database. @@ -97,19 +100,8 @@ public PolarisEclipseLinkMetaStoreSessionImpl( @NotNull RealmContext realmContext, @Nullable String confFile, @Nullable String persistenceUnitName) { - persistenceUnitName = persistenceUnitName == null ? "polaris" : persistenceUnitName; - Map properties = - loadProperties( - confFile == null ? "META-INF/persistence.xml" : confFile, persistenceUnitName); - // Replace database name in JDBC URL with realm - if (properties.containsKey(JDBC_URL)) { - properties.put( - JDBC_URL, properties.get(JDBC_URL).replace("{realm}", realmContext.getRealmIdentifier())); - } - properties.put(ECLIPSELINK_PERSISTENCE_XML, confFile); - - emf = Persistence.createEntityManagerFactory(persistenceUnitName, properties); + emf = createEntityManagerFactory(realmContext, confFile, persistenceUnitName); LOG.debug("Create EclipseLink Meta Store Session for {}", realmContext.getRealmIdentifier()); // init store @@ -118,13 +110,55 @@ public PolarisEclipseLinkMetaStoreSessionImpl( } /** Load the persistence unit properties from a given configuration file */ - private Map loadProperties(String confFile, String persistenceUnitName) { - if (properties != null) { - return properties; + private EntityManagerFactory createEntityManagerFactory( + @NotNull RealmContext realmContext, + @Nullable String confFile, + @Nullable String persistenceUnitName) { + ClassLoader prevClassLoader = Thread.currentThread().getContextClassLoader(); + try { + persistenceUnitName = persistenceUnitName == null ? "polaris" : persistenceUnitName; + confFile = confFile == null ? "META-INF/persistence.xml" : confFile; + + // Currently eclipseLink can only support configuration as a resource inside a jar. To support + // external configuration, persistence.xml needs be placed inside a jar and here is to add the + // jar to the classpath. + // Supported configuration file: META-INFO/persistence.xml, /tmp/conf.jar!/persistence.xml + int splitPosition = confFile.indexOf("!/"); + if (splitPosition != -1) { + String jarPrefixPath = confFile.substring(0, splitPosition); + confFile = confFile.substring(splitPosition + 2); + URL prefixUrl = this.getClass().getClassLoader().getResource(jarPrefixPath); + if (prefixUrl == null) { + prefixUrl = new File(jarPrefixPath).toURI().toURL(); + } + URLClassLoader currentClassLoader = + new URLClassLoader(new URL[] {prefixUrl}, this.getClass().getClassLoader()); + Thread.currentThread().setContextClassLoader(currentClassLoader); + } + + Map properties = loadProperties(confFile, persistenceUnitName); + // Replace database name in JDBC URL with realm + if (properties.containsKey(JDBC_URL)) { + properties.put( + JDBC_URL, + properties.get(JDBC_URL).replace("{realm}", realmContext.getRealmIdentifier())); + } + properties.put(ECLIPSELINK_PERSISTENCE_XML, confFile); + + return Persistence.createEntityManagerFactory(persistenceUnitName, properties); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + Thread.currentThread().setContextClassLoader(prevClassLoader); } + } + /** Load the persistence unit properties from a given configuration file */ + private Map loadProperties( + @NotNull String confFile, @NotNull String persistenceUnitName) throws Exception { try { - InputStream input = this.getClass().getClassLoader().getResourceAsStream(confFile); + InputStream input = + Thread.currentThread().getContextClassLoader().getResourceAsStream(confFile); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse(input); @@ -141,16 +175,15 @@ private Map loadProperties(String confFile, String persistenceUn nodeMap.getNamedItem("value").getNodeValue()); } - PolarisEclipseLinkMetaStoreSessionImpl.properties = properties; return properties; - } catch (Exception e) { - LOG.warn( - "Cannot find or parse the configuration file {} for persistence-unit {}", - confFile, - persistenceUnitName); + } catch (SAXException | IOException e) { + String str = + String.format( + "Cannot find or parse the configuration file %s for persistence-unit %s", + confFile, persistenceUnitName); + LOG.error(str); + throw new Exception(str); } - - return Maps.newHashMap(); } /** {@inheritDoc} */ diff --git a/extension/persistence/eclipselink/src/test/data/eclipselink_conf.jar b/extension/persistence/eclipselink/src/test/data/eclipselink_conf.jar new file mode 100644 index 0000000000000000000000000000000000000000..7f0a47fd7e9e1ebfa0a74e5226605141c954d286 GIT binary patch literal 1322 zcmWIWW@h1HVBlb2(2i$`WIzI(3@i-3t|5-Po_=on|4uP51OSzaFmP~iU?>#Zg7f0buK?pwji~r zIJ3AUH7_|;uOc^RX=q^aEjNL>=ixbDr<{opT3Gucz^5(gPL|Vw=7P&Mm3^0$&e^@w zD_3W^{IdT2ya}9s%Z$s;bnm!*H-G=#zgwjC+|O5ibHQri)zmL~FWnltUOTP$WV9P~i2sRdEeox7 z){s>W+;z%K=;W z$Br5gri5}WvvAV=v+U%hBo)~oca&aCO@DIc#N7*-weQ%@2;Y3R{o}nam$$5Y-D2t^ zoO?P%?r)6M*<+IBPyV;Qoai}aTfV@}koBTQyH5Xu?}784ip&0(qWgc&eZ7a5 zGd4<`opw{D{U_70&o@K2oq1+#6?-`2o1|UJ=GEK|Swg7?_ik*o4HQ|}7!k0&=!p8u z>%TmXOFl``wGdr;s%t98QniyS46>Wcv|}U|DHpl2FS)^WzNYwNtgA}5g_`9|gYxHw zJ#SU^#RKUM)mmt{JjH9O_xU8{S<%85?pqgfaXnsLpXYY{b=m!; z_4RX}nVi<`ZY`Q;d(84;`Pr`_pWEISZ$9+-&bj~hzrNdb@}B5L1CO_R{_F@k)SMK( zr>AGy87^`09o+|_m&rVCjCjM}cBb3YZhvf#dimv_6(*`H6mB+dtWsZb)_Ik=qh9Mm zv-zqX?|8lQ_RY(W`tMlPWy4f>a*j0D+$|<6bbgCuuI8Si_ftYn_-U&>&!yxm{mMNn z4$1D5OW&iI`{WI s3D=5L@_;OdnE)y&kR1jpDG*>ikcn1q1bDNuffO(U;VvK@#RB310QdbG^#A|> literal 0 HcmV?d00001 diff --git a/extension/persistence/eclipselink/src/test/java/com/snowflake/polaris/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreTest.java b/extension/persistence/eclipselink/src/test/java/com/snowflake/polaris/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreTest.java index 03cc1026e..7d707fc6a 100644 --- a/extension/persistence/eclipselink/src/test/java/com/snowflake/polaris/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreTest.java +++ b/extension/persistence/eclipselink/src/test/java/com/snowflake/polaris/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreTest.java @@ -15,6 +15,10 @@ */ package com.snowflake.polaris.persistence.impl.eclipselink; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + import io.polaris.core.PolarisCallContext; import io.polaris.core.PolarisConfigurationStore; import io.polaris.core.PolarisDefaultDiagServiceImpl; @@ -25,6 +29,12 @@ import io.polaris.extension.persistence.impl.eclipselink.PolarisEclipseLinkMetaStoreSessionImpl; import io.polaris.extension.persistence.impl.eclipselink.PolarisEclipseLinkStore; import java.time.ZoneId; +import java.util.stream.Stream; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; import org.mockito.Mockito; /** @@ -49,4 +59,30 @@ protected PolarisTestMetaStoreManager createPolarisTestMetaStoreManager() { new PolarisConfigurationStore() {}, timeSource.withZone(ZoneId.systemDefault()))); } + + @ParameterizedTest() + @ArgumentsSource(CreateStoreSessionArgs.class) + void testCreateStoreSession(String confFile, boolean success) { + PolarisDiagnostics diagServices = new PolarisDefaultDiagServiceImpl(); + PolarisEclipseLinkStore store = new PolarisEclipseLinkStore(diagServices); + try { + var session = + new PolarisEclipseLinkMetaStoreSessionImpl( + store, Mockito.mock(), () -> "realm", confFile, "polaris-dev"); + assertNotNull(session); + assertTrue(success); + } catch (Exception e) { + assertFalse(success); + } + } + + private static class CreateStoreSessionArgs implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext extensionContext) { + return Stream.of( + Arguments.of("META-INF/persistence.xml", true), + Arguments.of("eclipselink_conf.jar!/persistence.xml", true), + Arguments.of("/dummy_path/conf.jar!/persistence.xml", false)); + } + } } diff --git a/polaris-server.yml b/polaris-server.yml index cdfeb7027..fba595668 100644 --- a/polaris-server.yml +++ b/polaris-server.yml @@ -90,7 +90,8 @@ defaultRealms: metaStoreManager: type: in-memory # type: eclipse-link # uncomment to use eclipse-link as metastore - # persistence-unit: polaris-dev + # persistence-unit: polaris + # TODO - avoid duplicating token broker config oauth2: diff --git a/polaris-service/src/main/resources/META-INF/services/io.polaris.core.persistence.MetaStoreManagerFactory b/polaris-service/src/main/resources/META-INF/services/io.polaris.core.persistence.MetaStoreManagerFactory index 795cbfb8c..00e5cbc69 100644 --- a/polaris-service/src/main/resources/META-INF/services/io.polaris.core.persistence.MetaStoreManagerFactory +++ b/polaris-service/src/main/resources/META-INF/services/io.polaris.core.persistence.MetaStoreManagerFactory @@ -14,7 +14,5 @@ # limitations under the License. # -io.polaris.extension.persistence.impl.hibernate.HibernatePolarisMetaStoreManagerFactory io.polaris.service.persistence.InMemoryPolarisMetaStoreManagerFactory -com.snowflake.polaris.persistence.impl.remote.RemotePolarisMetaStoreManagerFactory io.polaris.extension.persistence.impl.eclipselink.EclipseLinkPolarisMetaStoreManagerFactory diff --git a/polaris-service/src/test/resources/META-INF/persistence.xml b/polaris-service/src/test/resources/META-INF/persistence.xml index 11828b284..db8c1c4bd 100644 --- a/polaris-service/src/test/resources/META-INF/persistence.xml +++ b/polaris-service/src/test/resources/META-INF/persistence.xml @@ -20,7 +20,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> - + org.eclipse.persistence.jpa.PersistenceProvider io.polaris.core.persistence.models.ModelEntity io.polaris.core.persistence.models.ModelEntityActive diff --git a/polaris-service/src/test/resources/polaris-server-integrationtest.yml b/polaris-service/src/test/resources/polaris-server-integrationtest.yml index 78ff8190d..1924cb0b1 100644 --- a/polaris-service/src/test/resources/polaris-server-integrationtest.yml +++ b/polaris-service/src/test/resources/polaris-server-integrationtest.yml @@ -85,6 +85,8 @@ metaStoreManager: type: in-memory # type: remote # url: http://sdp-devvm-mcollado:8080 +# type: eclipse-link # uncomment to use eclipse-link as metastore +# persistence-unit: polaris-dev oauth2: type: default