/* * Copyright 2008-2014 the original author or authors * * 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.kaleidofoundry.core.persistence; import java.util.Map.Entry; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceException; import javax.persistence.PersistenceUnit; import org.kaleidofoundry.core.lang.annotation.NotNull; import org.kaleidofoundry.core.lang.annotation.Nullable; import org.kaleidofoundry.core.util.Registry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.kaleidofoundry.core.env.model.EnvironmentConstants.KALEIDO_PERSISTENT_UNIT_NAME; /** * Factory used to manage {@link EntityManagerFactory} and {@link EntityManager} in an <b>unmanaged</b> environment<br/> * You can use it in container that does not managed injection via {@link PersistenceContext} or {@link PersistenceUnit} annotation, * like : * <ul> * <li>unit test class * <li>main class program * <li>... * </ul> * <b>Warning :</b> * <p> * If you get (so create) an EntityManager or an EntityManagerFactory with this helper class : <br/> * <b>you will have to close EntityManager or EntityManagerFactory by using </b>{@link UnmanagedEntityManagerFactory#close(EntityManager)} * or {@link UnmanagedEntityManagerFactory#close(EntityManagerFactory)} * </p> * * @author jraduget */ public abstract class UnmanagedEntityManagerFactory { private static Logger LOGGER = LoggerFactory.getLogger(UnmanagedEntityManagerFactory.class); // default kaleidofoundry EntityManagerFactory private static EntityManagerFactory DefaultEmf; // default kaleidofoundry threadlocal EntityManager private static final ThreadLocal<EntityManager> DefaultEm = new ThreadLocal<EntityManager>(); // registry of custom entityManager factory private static final Registry<String, EntityManagerFactory> CustomEmfRegistry = new Registry<String, EntityManagerFactory>(); // registry of custom entityManager factory private static final ThreadLocal<Registry<String, EntityManager>> CustomEmRegistry = new ThreadLocal<Registry<String, EntityManager>>(); /** * @return kaleido default entity manager (thread local)<br/> * it will create the EntityManager if needed. If previous entity manager thread was closed, a new one is created. * it will use as persistent unit name : {@link #KALEIDO_PERSISTENT_UNIT_NAME} <br/> */ @NotNull public static final EntityManager currentEntityManager() { EntityManager em = DefaultEm.get(); if (em == null || !em.isOpen()) { EntityManager lem = getEntityManagerFactory().createEntityManager(); DefaultEm.set(lem); return lem; } else { return em; } } /** * @param persistenceUnitName * @return entity manager which will map to the persistence unit name declared in META-INF/persistence.xml<br/> * <br/> * it will create the EntityManager if needed */ @NotNull public static final EntityManager currentEntityManager(@NotNull final String persistenceUnitName) { if (KALEIDO_PERSISTENT_UNIT_NAME.equals(persistenceUnitName)) { return currentEntityManager(); } else { // if none registry have been created yet if (CustomEmRegistry.get() == null) { CustomEmRegistry.set(new Registry<String, EntityManager>()); } // get current entityManager thread EntityManager em = CustomEmRegistry.get().get(persistenceUnitName); // create entityManager if needed (if null or closed) if (em == null || !em.isOpen()) { em = getEntityManagerFactory(persistenceUnitName).createEntityManager(); CustomEmRegistry.get().put(persistenceUnitName, em); } return em; } } /** * @return kaleido default entity manager factory<br/> * it will use as persistent unit name : {@link #KALEIDO_PERSISTENT_UNIT_NAME} <br/> * it will create the EntityManagerFactory if needed */ @NotNull public static final EntityManagerFactory getEntityManagerFactory() { if (DefaultEmf == null) { synchronized (UnmanagedEntityManagerFactory.class) { DefaultEmf = Persistence.createEntityManagerFactory(KALEIDO_PERSISTENT_UNIT_NAME); if (DefaultEmf == null) { throw new PersistenceException("JPA provider return a null entity manager factory"); } } } return DefaultEmf; } /** * @param persistenceUnitName name of the persistence unit declare in persistence.xml * @return entity manager factory which will map to the persistence unit name declared in META-INF/persistence.xml<br/> * it will create the EntityManagerFactory if needed */ @NotNull public static final EntityManagerFactory getEntityManagerFactory(@NotNull final String persistenceUnitName) { if (KALEIDO_PERSISTENT_UNIT_NAME.equals(persistenceUnitName)) { return getEntityManagerFactory(); } else { EntityManagerFactory emf = CustomEmfRegistry.get(persistenceUnitName); if (emf == null) { emf = Persistence.createEntityManagerFactory(persistenceUnitName); if (emf == null) { throw new PersistenceException("JPA provider return a null entity manager factory"); } CustomEmfRegistry.put(persistenceUnitName, emf); } return emf; } } /** * @param em close the given entity manager (if null, it does nothing) * @throws IllegalArgumentException if the given entity manager have not been create by {@link UnmanagedEntityManagerFactory} * @throws IllegalStateException if the entity manager is container-managed */ public static final void close(@Nullable final EntityManager em) throws IllegalArgumentException { if (em == null) { return; } // if em is the default kaleido one if (em == DefaultEm.get()) { em.close(); DefaultEm.remove(); } // browse custom threadlocal em to find it else { boolean foundEm = false; for (Entry<String, EntityManager> entry : CustomEmRegistry.get().entrySet()) { if (entry.getValue() == em) { em.close(); CustomEmRegistry.get().remove(entry.getKey()); foundEm = true; return; } } if (!foundEm) { throw new IllegalArgumentException("entityManager argument is not handle by UnmanagedEntityManagerFactory"); } } } /** * @param emf close the given entity manager factory if null, it does nothing * @throws IllegalArgumentException if the given entity manager factory have not been create by {@link UnmanagedEntityManagerFactory} * @throws IllegalStateException if the entity manager factory has been closed */ public static final void close(@Nullable final EntityManagerFactory emf) throws IllegalArgumentException { if (emf == null) { return; } // if emf is the default kaleido one if (emf == DefaultEmf) { emf.close(); DefaultEmf = null; } // browse custom emf to find it else { String emfNameUnitName = null; for (java.util.Map.Entry<String, EntityManagerFactory> lemf : CustomEmfRegistry.entrySet()) { if (lemf.getValue() == emf) { emfNameUnitName = lemf.getKey(); break; } } if (emfNameUnitName != null) { emf.close(); CustomEmfRegistry.remove(emfNameUnitName); } else { throw new IllegalArgumentException("entityManagerFactory argument is not handle by UnmanagedEntityManagerFactory"); } } } /** * close the given entity manager <b>silently</b> : no exception will be throws, only a logged error level message * * @param em * @see #close(EntityManager) */ public static final void closeItSilently(final EntityManager em) { try { close(em); } catch (Throwable th) { LOGGER.error("closeItSilently", th); } } /** * close the given entity manager factory <b>silently</b> : no exception will be throws, only a logged error level message * * @param emf * @see #close(EntityManager) */ public static final void closeItSilently(final EntityManagerFactory emf) { try { close(emf); } catch (Throwable th) { LOGGER.error("closeItSilently", th); } } /** * Close all open entity manager * * @throws IllegalArgumentException */ public static final void closeAll() { close(DefaultEm.get()); if (CustomEmRegistry.get() != null) { for (EntityManager em : CustomEmRegistry.get().values()) { close(em); } } close(DefaultEmf); for (EntityManagerFactory emf : CustomEmfRegistry.values()) { close(emf); } } }