/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.env.jpa; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.spi.PersistenceProvider; import javax.persistence.spi.PersistenceUnitTransactionType; import com.caucho.amber.manager.AmberPersistenceProvider; import com.caucho.config.Config; import com.caucho.config.ConfigException; import com.caucho.config.LineConfigException; import com.caucho.config.Names; import com.caucho.config.inject.BeanBuilder; import com.caucho.config.inject.CurrentLiteral; import com.caucho.config.inject.InjectManager; import com.caucho.config.program.ConfigProgram; import com.caucho.inject.Module; import com.caucho.loader.DynamicClassLoader; import com.caucho.loader.Environment; import com.caucho.loader.EnvironmentClassLoader; import com.caucho.loader.EnvironmentEnhancerListener; import com.caucho.loader.EnvironmentLocal; import com.caucho.loader.enhancer.ScanClass; import com.caucho.loader.enhancer.ScanListener; import com.caucho.util.CharBuffer; import com.caucho.util.IoUtil; import com.caucho.util.L10N; import com.caucho.vfs.Path; import com.caucho.vfs.ReadStream; import com.caucho.vfs.Vfs; /** * Manages the JPA persistence contexts. */ @Module public class PersistenceManager implements ScanListener, EnvironmentEnhancerListener { private static final Logger log = Logger.getLogger(PersistenceManager.class.getName()); private static final L10N L = new L10N(PersistenceManager.class); private static final EnvironmentLocal<PersistenceManager> _localManager = new EnvironmentLocal<PersistenceManager>(); private EnvironmentClassLoader _classLoader; private ClassLoader _tempLoader; private HashMap<String, PersistenceUnitManager> _persistenceUnitMap = new HashMap<String, PersistenceUnitManager>(); private ArrayList<ConfigProgram> _unitDefaultList = new ArrayList<ConfigProgram>(); private HashMap<String, ArrayList<ConfigProgram>> _unitDefaultMap = new HashMap<String, ArrayList<ConfigProgram>>(); private ArrayList<Path> _pendingRootList = new ArrayList<Path>(); private HashMap<String, EntityManager> _persistenceContextMap = new HashMap<String, EntityManager>(); private ArrayList<LazyEntityManagerFactory> _pendingFactoryList = new ArrayList<LazyEntityManagerFactory>(); private PersistenceManager(ClassLoader loader) { _classLoader = Environment.getEnvironmentClassLoader(loader); _localManager.set(this, _classLoader); _tempLoader = _classLoader.getNewTempClassLoader(); _classLoader.addScanListener(this); Environment.addEnvironmentListener(this, _classLoader); try { if (_classLoader instanceof DynamicClassLoader) ((DynamicClassLoader) _classLoader).make(); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } /** * Returns the local container. */ public static PersistenceManager create() { return create(Thread.currentThread().getContextClassLoader()); } /** * Returns the local container. */ public static PersistenceManager create(ClassLoader loader) { synchronized (_localManager) { PersistenceManager container = _localManager.getLevel(loader); if (container == null) { container = new PersistenceManager(loader); _localManager.set(container, loader); } return container; } } /** * Returns the local container. */ public static PersistenceManager getCurrent() { return getCurrent(Thread.currentThread().getContextClassLoader()); } /** * Returns the current environment container. */ public static PersistenceManager getCurrent(ClassLoader loader) { synchronized (_localManager) { return _localManager.get(loader); } } /** * Returns the environment's class loader */ public EnvironmentClassLoader getClassLoader() { return _classLoader; } /** * Returns the JClassLoader. */ public ClassLoader getTempClassLoader() { return _tempLoader; } /** * Adds a persistence-unit default */ public void addPersistenceUnitDefault(ConfigProgram program) { _unitDefaultList.add(program); } /** * Returns the persistence-unit default list. */ public ArrayList<ConfigProgram> getPersistenceUnitDefaults() { return _unitDefaultList; } void addPersistenceUnit(String name, ConfigJpaPersistenceUnit configJpaPersistenceUnit) { PersistenceUnitManager pUnit; pUnit = createPersistenceUnit(name, PersistenceUnitTransactionType.JTA); if (pUnit.getRoot() == null) pUnit.setRoot(configJpaPersistenceUnit.getPath()); pUnit.addOverrideProgram(configJpaPersistenceUnit.getProgram()); } /** * Adds a persistence-unit default */ public void addPersistenceUnitProxy(String name, ArrayList<ConfigProgram> program) { ArrayList<ConfigProgram> oldProgram = _unitDefaultMap.get(name); if (oldProgram == null) oldProgram = new ArrayList<ConfigProgram>(); oldProgram.addAll(program); _unitDefaultMap.put(name, oldProgram); } public ArrayList<ConfigProgram> getProxyProgram(String name) { return _unitDefaultMap.get(name); } public Class<?> loadTempClass(String name) throws ClassNotFoundException { return Class.forName(name, false, getTempClassLoader()); } public void init() { } public void start() { configurePersistenceRoots(); startPersistenceUnits(); } public void configurePersistenceRoots() { ArrayList<Path> rootList = new ArrayList<Path>(); synchronized (_pendingRootList) { rootList.addAll(_pendingRootList); _pendingRootList.clear(); } for (Path root : rootList) { parsePersistenceConfig(root); } } /** * Adds a persistence root. */ private void parsePersistenceConfig(Path root) { Path persistenceXml = root.lookup("META-INF/persistence.xml"); if (root.getFullPath().endsWith("WEB-INF/classes/") && ! persistenceXml.canRead()) { persistenceXml = root.lookup("../persistence.xml"); } if (! persistenceXml.canRead()) return; persistenceXml.setUserPath(persistenceXml.getURL()); if (log.isLoggable(Level.FINE)) log.fine(this + " parsing " + persistenceXml.getURL()); InputStream is = null; try { is = persistenceXml.openRead(); ConfigPersistence persistence = new ConfigPersistence(root); new Config().configure(persistence, is, "com/caucho/amber/cfg/persistence-31.rnc"); for (ConfigPersistenceUnit unitConfig : persistence.getUnitList()) { PersistenceUnitManager pUnit = createPersistenceUnit(unitConfig.getName(), unitConfig.getTransactionType()); if (pUnit.getRoot() == null) pUnit.setRoot(unitConfig.getRoot()); if (unitConfig.getVersion() != null) pUnit.setVersion(unitConfig.getVersion()); pUnit.setPersistenceXmlProgram(unitConfig.getProgram()); } } catch (RuntimeException e) { throw e; } catch (Exception e) { throw LineConfigException.create(e); } finally { try { if (is != null) is.close(); } catch (Exception e) { } } } private PersistenceUnitManager createPersistenceUnit(String name, PersistenceUnitTransactionType transactionType) { PersistenceUnitManager unit; synchronized (_persistenceUnitMap) { unit = _persistenceUnitMap.get(name); if (unit != null) return unit; unit = new PersistenceUnitManager(this, name, transactionType); _persistenceUnitMap.put(name, unit); } registerPersistenceUnit(unit); return unit; } /** * Adds the URLs for the classpath. */ public void startPersistenceUnits() { Thread thread = Thread.currentThread(); ClassLoader oldLoader = thread.getContextClassLoader(); try { // jpa/1630 thread.setContextClassLoader(_classLoader); InjectManager cdiManager = InjectManager.getCurrent(); cdiManager.updateResources(); ArrayList<PersistenceUnitManager> pUnitList = new ArrayList<PersistenceUnitManager>(); synchronized (_persistenceUnitMap) { pUnitList.addAll(_persistenceUnitMap.values()); } for (PersistenceUnitManager pUnit : pUnitList) { try { pUnit.start(); } catch (RuntimeException e) { if (pUnit.getJtaDataSource() == null && pUnit.getNonJtaDataSource() == null) { // env/0e22 #4491 log.warning(e.toString()); log.log(Level.FINER, e.toString(), e); } else { throw e; } } } } finally { thread.setContextClassLoader(oldLoader); } } public void close() { Thread thread = Thread.currentThread(); ClassLoader oldLoader = thread.getContextClassLoader(); try { // jpa/1630 thread.setContextClassLoader(_classLoader); ArrayList<PersistenceUnitManager> pUnitList = new ArrayList<PersistenceUnitManager>(); synchronized (_persistenceUnitMap) { pUnitList.addAll(_persistenceUnitMap.values()); } for (PersistenceUnitManager pUnit : pUnitList) { pUnit.close(); } } finally { thread.setContextClassLoader(oldLoader); } } private void registerPersistenceUnit(PersistenceUnitManager pUnit) { try { InjectManager beanManager = InjectManager.create(_classLoader); BeanBuilder<EntityManagerFactory> emfFactory; emfFactory = beanManager.createBeanFactory(EntityManagerFactory.class); emfFactory.qualifier(CurrentLiteral.CURRENT); emfFactory.qualifier(Names.create(pUnit.getName())); beanManager.addBean(emfFactory.singleton(pUnit.getEntityManagerFactoryProxy())); if (pUnit.getTransactionType() == PersistenceUnitTransactionType.JTA) { log.finer(L.l("register persistent-unit `{0}' with transaction-type JTA", pUnit.getName())); BeanBuilder<EntityManager> emFactory; emFactory = beanManager.createBeanFactory(EntityManager.class); emFactory.qualifier(CurrentLiteral.CURRENT); emFactory.qualifier(Names.create(pUnit.getName())); beanManager.addBean(emFactory.singleton(pUnit.getEntityManagerJtaProxy())); } else { log.finer(L.l("register persistent-unit `{0}' with transaction-type RESOURCE_LOCAL", pUnit.getName())); } /* factory = manager.createBeanFactory(EntityManager.class); factory.binding(CurrentLiteral.CURRENT); factory.binding(Names.create(unitName)); */ /* PersistenceContextComponent pcComp = new PersistenceContextComponent(unitName, persistenceContext); */ // manager.addBean(factory.singleton(persistenceContext)); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw ConfigException.create(e); } } void addProviderUnit(ConfigPersistenceUnit unit) { try { Class<?> cl = unit.getProvider(); if (cl == null) cl = getServiceProvider(); if (cl == null) cl = AmberPersistenceProvider.class; if (log.isLoggable(Level.CONFIG)) { log.config("JPA PersistenceUnit[" + unit.getName() + "] handled by " + cl.getName()); } PersistenceProvider provider = (PersistenceProvider) cl.newInstance(); String unitName = unit.getName(); Map props = null; /* synchronized (this) { LazyEntityManagerFactory lazyFactory = new LazyEntityManagerFactory(unit, provider, props); _pendingFactoryList.add(lazyFactory); } */ /* EntityManagerTransactionProxy persistenceContext = new EntityManagerTransactionProxy(this, unitName, props); _persistenceContextMap.put(unitName, persistenceContext); InjectManager manager = InjectManager.create(_classLoader); BeanFactory factory; factory = manager.createBeanFactory(EntityManagerFactory.class); */ /* EntityManagerFactoryComponent emf = new EntityManagerFactoryComponent(manager, this, provider, unit); */ /* EntityManagerFactoryProxy emf = new EntityManagerFactoryProxy(this, unitName); factory.binding(CurrentLiteral.CURRENT); factory.binding(Names.create(unitName)); manager.addBean(factory.singleton(emf)); factory = manager.createBeanFactory(EntityManager.class); factory.binding(CurrentLiteral.CURRENT); factory.binding(Names.create(unitName)); */ /* PersistenceContextComponent pcComp = new PersistenceContextComponent(unitName, persistenceContext); */ // manager.addBean(factory.singleton(persistenceContext)); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw ConfigException.create(e); } } Class<?> getServiceProvider() { ClassLoader loader = Thread.currentThread().getContextClassLoader(); try { Enumeration<URL> e = loader.getResources("META-INF/services/" + PersistenceProvider.class.getName()); while (e.hasMoreElements()) { URL url = e.nextElement(); Class<?> providerClass = loadProvider(url); if (providerClass != null && ! providerClass.equals(AmberPersistenceProvider.class)) { return providerClass; } } } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } return null; } private Class<?> loadProvider(URL url) { ClassLoader loader = Thread.currentThread().getContextClassLoader(); InputStream is = null; try { is = url.openStream(); ReadStream in = Vfs.openRead(is); String line; while ((line = in.readLine()) != null) { line = line.trim(); if (! "".equals(line) && ! line.startsWith("#")) { return Class.forName(line, false, loader); } } } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } finally { IoUtil.close(is); } return null; } // // private // // ScanListener // /** * Since JPA enhances, it is priority 0 */ @Override public int getScanPriority() { return 0; } /** * Returns true if the root is a valid scannable root. */ @Override public boolean isRootScannable(Path root, String packageRoot) { if (root.lookup("META-INF/persistence.xml").canRead() || (root.getFullPath().endsWith("WEB-INF/classes/") && root.lookup("../persistence.xml").canRead())) { _pendingRootList.add(root); } return false; } @Override public ScanClass scanClass(Path root, String packageRoot, String className, int modifiers) { return null; } @Override public boolean isScanMatchAnnotation(CharBuffer annotationName) { return false; } /** * Callback to note the class matches */ @Override public void classMatchEvent(EnvironmentClassLoader loader, Path root, String className) { } // // EnvironmentListener // /** * Handles the environment config phase */ @Override public void environmentConfigureEnhancer(EnvironmentClassLoader loader) { configurePersistenceRoots(); // env/0h31 startPersistenceUnits(); } /** * Handles the environment config phase */ @Override public void environmentConfigure(EnvironmentClassLoader loader) { configurePersistenceRoots(); } /** * Handles the environment config phase */ @Override public void environmentBind(EnvironmentClassLoader loader) { configurePersistenceRoots(); } /** * Handles the case where the environment is starting (after init). */ @Override public void environmentStart(EnvironmentClassLoader loader) { start(); } /** * Handles the case where the environment is stopping */ @Override public void environmentStop(EnvironmentClassLoader loader) { close(); } @Override public String toString() { return getClass().getSimpleName() + "[" + _classLoader.getId() + "]"; } class LazyEntityManagerFactory { private final ConfigPersistenceUnit _unit; private final PersistenceProvider _provider; private final Map _props; LazyEntityManagerFactory(ConfigPersistenceUnit unit, PersistenceProvider provider, Map props) { _unit = unit; _provider = provider; _props = props; } void init() { /* synchronized (ManagerPersistence.this) { String unitName = _unit.getName(); EntityManagerFactory factory = _factoryMap.get(unitName); if (factory == null) { factory = _provider.createContainerEntityManagerFactory(_unit, _props); if (factory == null) throw new ConfigException(L.l( "'{0}' must return an EntityManagerFactory", _provider.getClass().getName())); if (log.isLoggable(Level.FINE)) { log.fine(L.l("{0} creating persistence unitName={1} created with provider '{2}'", this, unitName, _provider.getClass().getName())); } _factoryMap.put(unitName, factory); } } */ } } }