package com.eucalyptus.entities; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.atomic.AtomicInteger; import javax.persistence.MappedSuperclass; import javax.persistence.PersistenceContext; import org.apache.log4j.Logger; import org.hibernate.ejb.Ejb3Configuration; import org.hibernate.ejb.EntityManagerFactoryImpl; import com.eucalyptus.bootstrap.BootstrapException; import com.eucalyptus.records.EventType; import com.eucalyptus.system.Ats; import com.eucalyptus.util.LogUtil; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimaps; import edu.emory.mathcs.backport.java.util.Collections; import com.eucalyptus.records.EventRecord; @SuppressWarnings( "unchecked" ) public class PersistenceContexts { public static int MAX_FAIL = 5; private static AtomicInteger failCount = new AtomicInteger( 0 ); private static Logger LOG = Logger.getLogger( PersistenceContexts.class ); private static final ArrayListMultimap<String, Class> entities = Multimaps.newArrayListMultimap( ); private static final List<Class> sharedEntities = Lists.newArrayList( ); private static Map<String, EntityManagerFactoryImpl> emf = new ConcurrentSkipListMap<String, EntityManagerFactoryImpl>( ); private static List<Exception> illegalAccesses = Collections.synchronizedList( Lists.newArrayList( ) ); static void addEntity( Class entity ) { if ( !isDuplicate( entity ) ) { String ctxName = Ats.from( entity ).get( PersistenceContext.class ).name( ); EventRecord.here( PersistenceContextDiscovery.class, EventType.PERSISTENCE_ENTITY_REGISTERED, ctxName, entity.getCanonicalName( ) ).info( ); entities.put( ctxName, entity ); } } static void addSharedEntity( Class entity ) { if ( !isDuplicate( entity ) ) { EventRecord.here( PersistenceContextDiscovery.class, EventType.PERSISTENCE_ENTITY_REGISTERED, "shared", entity.getCanonicalName( ) ).info( ); sharedEntities.add( entity ); } } private static boolean isDuplicate( Class entity ) { PersistenceContext ctx = Ats.from( entity ).get( PersistenceContext.class ); if( Ats.from( entity ).has( MappedSuperclass.class ) ) { return false; } else if ( ctx == null || ctx.name( ) == null ) { RuntimeException ex = new RuntimeException( "Failed to register broken entity class: " + entity.getCanonicalName( ) + ". Ensure that the class has a well-formed @PersistenceContext annotation."); LOG.error( ex, ex ); return false; } else if ( sharedEntities.contains( entity ) ) { Class old = sharedEntities.get( sharedEntities.indexOf( entity ) ); LOG.error( "Duplicate entity definition detected: " + entity.getCanonicalName( ) ); LOG.error( "=> OLD: " + old.getProtectionDomain( ).getCodeSource( ).getLocation( ) ); LOG.error( "=> NEW: " + entity.getProtectionDomain( ).getCodeSource( ).getLocation( ) ); throw BootstrapException.throwFatal( "Duplicate entity definition in shared entities: " + entity.getCanonicalName( ) + ". See error logs for details." ); } else if ( entities.get( ctx.name( ) ) != null && entities.get( ctx.name( ) ).contains( entity ) ) { List<Class> context = entities.get( ctx.name( ) ); Class old = context.get( context.indexOf( entity ) ); LOG.error( "Duplicate entity definition detected: " + entity.getCanonicalName( ) ); LOG.error( "=> OLD: " + old.getProtectionDomain( ).getCodeSource( ).getLocation( ) ); LOG.error( "=> NEW: " + entity.getProtectionDomain( ).getCodeSource( ).getLocation( ) ); throw BootstrapException.throwFatal( "Duplicate entity definition in '" + ctx.name( ) + "': " + entity.getCanonicalName( ) + ". See error logs for details." ); } else { return false; } } public static EntityManagerFactoryImpl registerPersistenceContext( final String persistenceContext, final Ejb3Configuration config ) { synchronized ( PersistenceContexts.class ) { if ( illegalAccesses != null && !illegalAccesses.isEmpty( ) ) { for ( Exception e : illegalAccesses ) { LOG.fatal( e, e ); } LogUtil.header( "Illegal Access to Persistence Context. Database not yet configured. This is always a BUG: " + persistenceContext ); System.exit( 1 ); } else if ( !emf.containsKey( persistenceContext ) ) { illegalAccesses = null; EntityManagerFactoryImpl entityManagerFactory = ( EntityManagerFactoryImpl ) config.buildEntityManagerFactory( ); LOG.info( "-> Setting up persistence context for : " + persistenceContext ); LOG.info( LogUtil.subheader( LogUtil.dumpObject( config ) ) ); emf.put( persistenceContext, entityManagerFactory ); } return emf.get( persistenceContext ); } } public static List<String> list( ) { return Lists.newArrayList( entities.keySet( ) ); } public static List<Class> listEntities( String persistenceContext ) { return entities.get( persistenceContext ); } public static void handleConnectionError( Throwable cause ) { touchDatabase( ); } private static void touchDatabase( ) { if ( MAX_FAIL > failCount.getAndIncrement( ) ) { LOG.fatal( LogUtil.header( "Database connection failure limit reached (" + MAX_FAIL + "): HUPping the system." ) ); System.exit( 123 ); } else { LOG.warn( LogUtil.subheader( "Error using or obtaining a database connection, fail count is " + failCount.intValue( ) + " (max=" + MAX_FAIL + ") more times before reloading." ) ); } } @SuppressWarnings( "deprecation" ) public static EntityManagerFactoryImpl getEntityManagerFactory( final String persistenceContext ) { if ( !emf.containsKey( persistenceContext ) ) { RuntimeException e = new RuntimeException( "Attempting to access an entity wrapper before the database has been configured: " + persistenceContext + ". The available contexts are: " + emf.keySet( )); illegalAccesses = illegalAccesses == null ? Collections.synchronizedList( Lists.newArrayList( ) ) : illegalAccesses; illegalAccesses.add( e ); throw e; } return emf.get( persistenceContext ); } public static void shutdown() { for( String ctx : emf.keySet( ) ) { EntityManagerFactoryImpl em = emf.get( ctx ); if( em.isOpen( ) ) { LOG.info( "Closing persistence context: " + ctx ); em.close( ); } else { LOG.info( "Closing persistence context: " + ctx + " (found it closed already)" ); } } } }