/* * Copyright 2016 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.keycloak.connections.jpa.util; import org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl; import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor; import org.hibernate.jpa.boot.internal.PersistenceXmlParser; import org.hibernate.jpa.boot.spi.Bootstrap; import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider; import org.keycloak.connections.jpa.entityprovider.ProxyClassLoader; import org.keycloak.models.KeycloakSession; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.spi.PersistenceUnitTransactionType; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; /** * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> */ public class JpaUtils { public static final String HIBERNATE_DEFAULT_SCHEMA = "hibernate.default_schema"; public static String getTableNameForNativeQuery(String tableName, EntityManager em) { String schema = (String) em.getEntityManagerFactory().getProperties().get(HIBERNATE_DEFAULT_SCHEMA); return (schema==null) ? tableName : schema + "." + tableName; } public static EntityManagerFactory createEntityManagerFactory(KeycloakSession session, String unitName, Map<String, Object> properties, ClassLoader classLoader, boolean jta) { PersistenceUnitTransactionType txType = jta ? PersistenceUnitTransactionType.JTA : PersistenceUnitTransactionType.RESOURCE_LOCAL; PersistenceXmlParser parser = new PersistenceXmlParser(new ClassLoaderServiceImpl(classLoader), txType); List<ParsedPersistenceXmlDescriptor> persistenceUnits = parser.doResolve(properties); for (ParsedPersistenceXmlDescriptor persistenceUnit : persistenceUnits) { if (persistenceUnit.getName().equals(unitName)) { List<Class<?>> providedEntities = getProvidedEntities(session); for (Class<?> entityClass : providedEntities) { // Add all extra entity classes to the persistence unit. persistenceUnit.addClasses(entityClass.getName()); } // Now build the entity manager factory, supplying a proxy classloader, so Hibernate will be able // to find and load the extra provided entities. Set the provided classloader as parent classloader. persistenceUnit.setTransactionType(txType); return Bootstrap.getEntityManagerFactoryBuilder(persistenceUnit, properties, new ProxyClassLoader(providedEntities, classLoader)).build(); } } throw new RuntimeException("Persistence unit '" + unitName + "' not found"); } /** * Get a list of all provided entities by looping over all configured entity providers. * * @param session the keycloak session * @return a list of all provided entities (can be an empty list) */ public static List<Class<?>> getProvidedEntities(KeycloakSession session) { List<Class<?>> providedEntityClasses = new ArrayList<>(); // Get all configured entity providers. Set<JpaEntityProvider> entityProviders = session.getAllProviders(JpaEntityProvider.class); // For every provider, add all entity classes to the list. for (JpaEntityProvider entityProvider : entityProviders) { providedEntityClasses.addAll(entityProvider.getEntities()); } return providedEntityClasses; } /** * Get the name of custom table for liquibase updates for give ID of JpaEntityProvider * @param jpaEntityProviderFactoryId * @return table name */ public static String getCustomChangelogTableName(String jpaEntityProviderFactoryId) { String upperCased = jpaEntityProviderFactoryId.toUpperCase(); upperCased = upperCased.replaceAll("-", "_"); upperCased = upperCased.replaceAll("[^A-Z_]", ""); return "DATABASECHANGELOG_" + upperCased.substring(0, Math.min(10, upperCased.length())); } }