5454import java .util .HashMap ;
5555import java .util .List ;
5656import java .util .Map ;
57+ import java .util .concurrent .ConcurrentHashMap ;
5758import java .util .function .Function ;
5859import java .util .function .Predicate ;
5960import java .util .function .Supplier ;
6061import java .util .stream .Collectors ;
6162import javax .xml .parsers .DocumentBuilder ;
6263import javax .xml .parsers .DocumentBuilderFactory ;
64+ import javax .xml .parsers .ParserConfigurationException ;
6365import javax .xml .xpath .XPath ;
6466import javax .xml .xpath .XPathConstants ;
67+ import javax .xml .xpath .XPathExpressionException ;
6568import javax .xml .xpath .XPathFactory ;
6669import org .jetbrains .annotations .NotNull ;
6770import org .jetbrains .annotations .Nullable ;
71+ import org .jetbrains .annotations .TestOnly ;
6872import org .slf4j .Logger ;
6973import org .slf4j .LoggerFactory ;
7074import org .w3c .dom .Document ;
@@ -80,6 +84,10 @@ public class PolarisEclipseLinkMetaStoreSessionImpl implements PolarisMetaStoreS
8084 private static final Logger LOG =
8185 LoggerFactory .getLogger (PolarisEclipseLinkMetaStoreSessionImpl .class );
8286
87+ // Cache to hold the EntityManagerFactory for each realm. Each realm needs a separate
88+ // EntityManagerFactory since it connects to different databases
89+ private static final ConcurrentHashMap <String , EntityManagerFactory > realmFactories =
90+ new ConcurrentHashMap <>();
8391 private final EntityManagerFactory emf ;
8492 private final ThreadLocal <EntityManager > localSession = new ThreadLocal <>();
8593 private final PolarisEclipseLinkStore store ;
@@ -100,20 +108,30 @@ public PolarisEclipseLinkMetaStoreSessionImpl(
100108 @ NotNull RealmContext realmContext ,
101109 @ Nullable String confFile ,
102110 @ Nullable String persistenceUnitName ) {
103-
104- emf = createEntityManagerFactory (realmContext , confFile , persistenceUnitName );
105111 LOG .debug ("Create EclipseLink Meta Store Session for {}" , realmContext .getRealmIdentifier ());
112+ emf = createEntityManagerFactory (realmContext , confFile , persistenceUnitName );
106113
107114 // init store
108115 this .store = store ;
109116 this .storageIntegrationProvider = storageIntegrationProvider ;
110117 }
111118
112- /** Load the persistence unit properties from a given configuration file */
119+ /**
120+ * Create EntityManagerFactory.
121+ *
122+ * <p>The EntityManagerFactory creation is expensive, so we are caching and reusing it for each
123+ * realm.
124+ */
113125 private EntityManagerFactory createEntityManagerFactory (
114126 @ NotNull RealmContext realmContext ,
115127 @ Nullable String confFile ,
116128 @ Nullable String persistenceUnitName ) {
129+ String realm = realmContext .getRealmIdentifier ();
130+ EntityManagerFactory factory = realmFactories .getOrDefault (realm , null );
131+ if (factory != null ) {
132+ return factory ;
133+ }
134+
117135 ClassLoader prevClassLoader = Thread .currentThread ().getContextClassLoader ();
118136 try {
119137 persistenceUnitName = persistenceUnitName == null ? "polaris" : persistenceUnitName ;
@@ -131,31 +149,44 @@ private EntityManagerFactory createEntityManagerFactory(
131149 if (prefixUrl == null ) {
132150 prefixUrl = new File (jarPrefixPath ).toURI ().toURL ();
133151 }
152+
153+ LOG .info (
154+ "Created a new ClassLoader with the jar {} in classpath to load the config file" ,
155+ prefixUrl );
156+
134157 URLClassLoader currentClassLoader =
135158 new URLClassLoader (new URL [] {prefixUrl }, this .getClass ().getClassLoader ());
159+
160+ LOG .debug ("Update ClassLoader in current thread temporarily" );
136161 Thread .currentThread ().setContextClassLoader (currentClassLoader );
137162 }
138163
139164 Map <String , String > properties = loadProperties (confFile , persistenceUnitName );
140165 // Replace database name in JDBC URL with realm
141166 if (properties .containsKey (JDBC_URL )) {
142- properties .put (
143- JDBC_URL ,
144- properties .get (JDBC_URL ).replace ("{realm}" , realmContext .getRealmIdentifier ()));
167+ properties .put (JDBC_URL , properties .get (JDBC_URL ).replace ("{realm}" , realm ));
145168 }
146169 properties .put (ECLIPSELINK_PERSISTENCE_XML , confFile );
147170
148- return Persistence .createEntityManagerFactory (persistenceUnitName , properties );
149- } catch (Exception e ) {
171+ factory = Persistence .createEntityManagerFactory (persistenceUnitName , properties );
172+ realmFactories .putIfAbsent (realm , factory );
173+
174+ return factory ;
175+ } catch (IOException e ) {
150176 throw new RuntimeException (e );
151177 } finally {
152178 Thread .currentThread ().setContextClassLoader (prevClassLoader );
153179 }
154180 }
155181
182+ @ TestOnly
183+ static void clearEntityManagerFactories () {
184+ realmFactories .clear ();
185+ }
186+
156187 /** Load the persistence unit properties from a given configuration file */
157188 private Map <String , String > loadProperties (
158- @ NotNull String confFile , @ NotNull String persistenceUnitName ) throws Exception {
189+ @ NotNull String confFile , @ NotNull String persistenceUnitName ) throws IOException {
159190 try {
160191 InputStream input =
161192 Thread .currentThread ().getContextClassLoader ().getResourceAsStream (confFile );
@@ -176,13 +207,16 @@ private Map<String, String> loadProperties(
176207 }
177208
178209 return properties ;
179- } catch (SAXException | IOException e ) {
210+ } catch (XPathExpressionException
211+ | ParserConfigurationException
212+ | SAXException
213+ | IOException e ) {
180214 String str =
181215 String .format (
182216 "Cannot find or parse the configuration file %s for persistence-unit %s" ,
183217 confFile , persistenceUnitName );
184- LOG .error (str );
185- throw new Exception (str );
218+ LOG .error (str , e );
219+ throw new IOException (str );
186220 }
187221 }
188222
0 commit comments