package com.eucalyptus.bootstrap;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.persistence.Entity;
import javax.persistence.PersistenceContext;
import org.apache.log4j.Logger;
import com.eucalyptus.records.EventRecord;
import com.eucalyptus.records.EventType;
import com.eucalyptus.system.Ats;
import com.eucalyptus.system.BaseDirectory;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
public abstract class ServiceJarDiscovery implements Comparable<ServiceJarDiscovery> {
private static Logger LOG = Logger.getLogger( ServiceJarDiscovery.class );
private static SortedSet<ServiceJarDiscovery> discovery = Sets.newTreeSet( );
private static Multimap<Class, String> classList = Multimaps.newArrayListMultimap( );
@SuppressWarnings( { "deprecation", "unchecked" } )
public static void processFile( File f ) throws IOException {
JarFile jar = new JarFile( f );
Properties props = new Properties( );
Enumeration<JarEntry> jarList = jar.entries( );
LOG.info( "-> Trying to load component info from " + f.getAbsolutePath( ) );
while ( jarList.hasMoreElements( ) ) {
JarEntry j = jarList.nextElement( );
if ( j.getName( ).matches( ".*\\.class.{0,1}" ) ) {
String classGuess = j.getName( ).replaceAll( "/", "." ).replaceAll( "\\.class.{0,1}", "" );
try {
Class candidate = ClassLoader.getSystemClassLoader( ).loadClass( classGuess );
classList.put( candidate, f.getAbsolutePath( ) );
if ( ServiceJarDiscovery.class.isAssignableFrom( candidate ) && !ServiceJarDiscovery.class.equals( candidate ) ) {
try {
ServiceJarDiscovery discover = ( ServiceJarDiscovery ) candidate.newInstance( );
discovery.add( discover );
EventRecord.here( ServiceJarDiscovery.class, EventType.BOOTSTRAP_INIT_DISCOVERY, discovery.getClass( ).getCanonicalName( ) );
} catch ( Exception e ) {
LOG.fatal( e, e );
jar.close( );
throw new RuntimeException( e );
}
}
} catch ( ClassNotFoundException e ) {
LOG.debug( e, e );
}
}
}
jar.close( );
}
private static void doDiscovery( ) {
File libDir = new File( BaseDirectory.LIB.toString( ) );
for ( File f : libDir.listFiles( ) ) {
if ( f.getName( ).startsWith( com.eucalyptus.bootstrap.Component.eucalyptus.name( ) ) && f.getName( ).endsWith( ".jar" )
&& !f.getName( ).matches( ".*-ext-.*" ) ) {
LOG.debug( "Found eucalyptus component jar: " + f.getName( ) );
try {
ServiceJarDiscovery.processFile( f );
} catch ( Throwable e ) {
LOG.error( e.getMessage( ) );
continue;
}
}
}
ServiceJarDiscovery.runDiscovery( );
}
public static void checkUniqueness( Class c ) {
if ( classList.get( c ).size( ) > 1 ) {
LOG.fatal( "Duplicate bootstrap class registration: " + c.getName( ) );
for ( String fileName : classList.get( c ) ) {
LOG.fatal( "\n==> Defined in: " + fileName );
}
System.exit( 1 );
}
}
public static void runDiscovery( ) {
for ( ServiceJarDiscovery s : discovery ) {
EventRecord.here( ServiceJarDiscovery.class, EventType.DISCOVERY_STARTED, s.getClass( ).getSimpleName( ) ).info( );
for ( Class c : classList.keySet( ) ) {
try {
s.checkClass( c );
} catch ( Throwable t ) {
LOG.debug( t, t );
}
}
}
for ( ServiceJarDiscovery s : discovery ) {
EventRecord.here( ServiceJarDiscovery.class, EventType.DISCOVERY_FINISHED, s.getClass( ).getSimpleName( ) ).info( );
}
}
public void checkClass( Class candidate ) {
try {
if ( this.processClass( candidate ) ) {
ServiceJarDiscovery.checkUniqueness( candidate );
EventRecord.here( ServiceJarDiscovery.class, EventType.DISCOVERY_LOADED_ENTRY, this.getClass( ).getSimpleName( ), candidate.getName( ) ).info( );
}
} catch ( Throwable e ) {
if ( e instanceof InstantiationException ) {} else {
LOG.trace( e, e );
}
}
}
/**
* Process the potential bootstrap-related class. Return false or throw an exception if the class is rejected.
*
* @param candidate
* @return TODO
* @throws Throwable
*/
public abstract boolean processClass( Class candidate ) throws Throwable;
public Double getDistinctPriority( ) {
return this.getPriority( ) + ( .1d / this.getClass( ).hashCode( ) );
}
public abstract Double getPriority( );
@Override
public int compareTo( ServiceJarDiscovery that ) {
return this.getDistinctPriority( ).compareTo( that.getDistinctPriority( ) );
}
public static URLClassLoader makeClassLoader( File libDir ) {
URLClassLoader loader = new URLClassLoader( Lists.transform( Arrays.asList( libDir.listFiles( ) ), new Function<File, URL>( ) {
@Override
public URL apply( File arg0 ) {
try {
return URI.create( "file://" + arg0.getAbsolutePath( ) ).toURL( );
} catch ( MalformedURLException e ) {
LOG.debug( e, e );
return null;
}
}
} ).toArray( new URL[] {} ) );
return loader;
}
public static List<String> contextsInDir( File libDir ) {
ClassLoader oldLoader = Thread.currentThread( ).getContextClassLoader( );
try {
Thread.currentThread( ).setContextClassLoader( makeClassLoader( libDir ) );
Set<String> ctxs = Sets.newHashSet( );
for( Class candidate : getClassList( libDir ) ) {
if ( Ats.from( candidate ).has( Entity.class ) ) {
if ( Ats.from( candidate ).has( PersistenceContext.class ) ) {
ctxs.add( Ats.from( candidate ).get( PersistenceContext.class ).name( ) );
}
}
}
return Lists.newArrayList( ctxs );
} finally {
Thread.currentThread( ).setContextClassLoader( oldLoader );
}
}
public static List<Class> classesInDir( File libDir ) {
ClassLoader oldLoader = Thread.currentThread( ).getContextClassLoader( );
try {
Thread.currentThread( ).setContextClassLoader( makeClassLoader( libDir ) );
return getClassList( libDir );
} finally {
Thread.currentThread( ).setContextClassLoader( oldLoader );
}
}
private static List<Class> getClassList( File libDir ) {
List<Class> classList = Lists.newArrayList( );
for ( File f : libDir.listFiles( ) ) {
if ( f.getName( ).startsWith( "eucalyptus" ) && f.getName( ).endsWith( ".jar" ) && !f.getName( ).matches( ".*-ext-.*" ) ) {
// LOG.trace( "Found eucalyptus component jar: " + f.getName( ) );
JarFile jar = null;
try {
jar = new JarFile( f );
Enumeration<JarEntry> jarList = jar.entries( );
while ( jarList.hasMoreElements( ) ) {
JarEntry j = jarList.nextElement( );
if ( j.getName( ).matches( ".*\\.class.{0,1}" ) ) {
String classGuess = j.getName( ).replaceAll( "/", "." ).replaceAll( "\\.class.{0,1}", "" );
try {
Class candidate = ClassLoader.getSystemClassLoader( ).loadClass( classGuess );
classList.add( candidate );
} catch ( ClassNotFoundException e ) {
// LOG.trace( e, e );
}
}
}
jar.close( );
} catch ( Throwable e ) {
LOG.error( e.getMessage( ) );
if ( jar != null ) {
try {
jar.close();
} catch ( IOException ex ) {
}
}
continue;
}
}
}
return classList;
}
}