/*************************************************************************
* Copyright 2009-2012 Eucalyptus Systems, Inc.
*
* This program 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; version 3 of the License.
*
* This program 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
* CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
* additional information or have any questions.
*
* This file may incorporate work covered under the following copyright
* and permission notice:
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, Regents of the University of California
* All rights reserved.
*
* Redistribution and use of this software in source and binary forms,
* with or without modification, are permitted provided that the
* following conditions are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE
* THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL,
* COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE,
* AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
* IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA,
* SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY,
* WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION,
* REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO
* IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT
* NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS.
*
* This file may incorporate work covered under the following copyright and
* permission notice:
*
* Copyright (c) 2003-2010, Dennis M. Sosnoski.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of JiBX nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
************************************************************************/
/**
* Binding classloader. This is intended to substitute for the System
* classloader (i.e., the one used for loading user classes). It first processes
* one or more binding definitions, caching the binary classes modified by the
* bindings. It then uses these modified forms of the classes when they're
* requested for loading.
*
* @author Dennis M. Sosnoski
* @author chris grzegorczyk <grze@eucalyptus.com>
*/
package com.eucalyptus.bootstrap;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.jibx.binding.Loader;
import org.jibx.binding.Utility;
import org.jibx.binding.classes.BoundClass;
import org.jibx.binding.classes.ClassCache;
import org.jibx.binding.classes.ClassFile;
import org.jibx.binding.classes.MungedClass;
import org.jibx.binding.def.BindingBuilder;
import org.jibx.binding.def.BindingDefinition;
import org.jibx.binding.model.BindingElement;
import org.jibx.binding.model.IncludeElement;
import org.jibx.binding.model.MappingElement;
import org.jibx.binding.model.MappingElementBase;
import org.jibx.binding.model.ValidationContext;
import org.jibx.runtime.JiBXException;
import org.jibx.runtime.impl.UnmarshallingContext;
import org.jibx.util.ClasspathUrlExtender;
import com.eucalyptus.system.SubDirectory;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.ByteSource;
import com.google.common.io.Resources;
public class BootstrapClassLoader extends URLClassLoader {
private static Logger LOG = Logger.getLogger( BootstrapClassLoader.class );
private static volatile BootstrapClassLoader singleton;
public static BootstrapClassLoader init( ) {
try {
if ( singleton == null ) {
if ( SubDirectory.CLASSCACHE.getFile( ).exists( ) ) {
try {
FileUtils.deleteDirectory( SubDirectory.CLASSCACHE.getFile( ) );
} catch ( IOException ex ) {
LOG.error( ex, ex );
}
}
SubDirectory.CLASSCACHE.check( );
String[] paths = Utility.getClassPaths( );
URL[] urls = new URL[paths.length];
for ( int i = 0; i < urls.length; i++ ) {
urls[i] = new File( paths[i] ).toURI( ).toURL( );
}
singleton = new BootstrapClassLoader( urls );
}
Thread.currentThread( ).setContextClassLoader( singleton );
return singleton;
} catch ( final MalformedURLException ex ) {
LOG.error( ex, ex );
return null;
}
}
private final List<BindingDefinition> bindings = Lists.newArrayList( );
private boolean isBound;
private final Map<String, ClassFile> classMap = Maps.newHashMap( );
public static BootstrapClassLoader getInstance( ) {
return init( );
}
private BootstrapClassLoader( URL[] urls ) throws MalformedURLException {
super( urls, ClassLoader.getSystemClassLoader( ) );
List<String> fpaths = Lists.newArrayList( );
for ( URL path : Loader.getClassPaths( ) ) {
LOG.debug( path );
if ( "file".equals( path.getProtocol( ) ) ) {
fpaths.add( path.getPath( ) );
}
}
// set paths to be used for loading referenced classes
String[] dirs = ( String[] ) fpaths.toArray( new String[0] );
ClassCache.setPaths( dirs );
ClassFile.setPaths( dirs );
ClasspathUrlExtender.setClassLoader( ClassFile.getClassLoader( ) );
// reset static information accumulation for binding
BoundClass.reset( );
MungedClass.reset( );
BindingDefinition.reset( );
}
public void reset( ) {
this.bindings.clear( );
this.classMap.clear( );
this.isBound = false;
BoundClass.reset( );
MungedClass.reset( );
BindingDefinition.reset( );
}
public static URL[] getClassPaths( ) throws MalformedURLException {
String[] paths = Utility.getClassPaths( );
URL[] urls = new URL[paths.length];
for ( int i = 0; i < urls.length; i++ ) {
urls[i] = new File( paths[i] ).toURI( ).toURL( );
}
return urls;
}
public void loadBinding( String fname, String sname, InputStream is, URL url ) throws JiBXException, IOException {
throw new IllegalStateException( "Call not allowed: only resources can currently be loaded." );
}
public void loadFileBinding( String path ) throws JiBXException, IOException {
throw new IllegalStateException( "Call not allowed: only resources can currently be loaded." );
}
public void loadResourceBinding( String path ) throws JiBXException, IOException {
if ( this.isBound ) {
throw new IllegalStateException( "Call not allowed after bindings compiled" );
} else {
URL url = Resources.getResource( path );
ByteSource inSupplier = Resources.asByteSource( url );
String fname = path;
int split = fname.lastIndexOf( '/' );
if ( split >= 0 ) {
fname = fname.substring( split + 1 );
}
String defaultBindingName = Utility.bindingFromFileName( fname );
try {
ValidationContext vctx = BindingElement.newValidationContext( );
BindingElement root = BindingElement.validateBinding( fname, url, inSupplier.openBufferedStream( ), vctx );
if ( vctx.getErrorCount( ) == 0 && vctx.getFatalCount( ) == 0 ) {
ClassFile classFile = findMappedClass( root );
String tpack = root.getTargetPackage( );
if ( tpack == null && classFile != null ) {
tpack = classFile.getPackage( );
}
String bindingName = root.getName( );
UnmarshallingContext uctx = new UnmarshallingContext( );
uctx.setDocument( inSupplier.openBufferedStream( ), fname, null );
if ( classFile != null ) {
bindingName = ( bindingName == null
? defaultBindingName
: bindingName );
BoundClass.setModify( classFile.getRoot( ), tpack, bindingName );
}
BindingDefinition bindingDefinition = BindingBuilder.unmarshalBindingDefinition( uctx, defaultBindingName, url );
File rootFile = null;
if ( tpack == null ) {
tpack = bindingDefinition.getDefaultPackage( );
}
if ( classFile == null ) {
rootFile = ClassCache.getModifiablePath( );
if ( root == null ) {
throw new IllegalStateException( "Need modifiable directory on classpath for storing generated factory class file" );
}
if ( tpack == null ) {
tpack = "";
}
} else {
rootFile = classFile.getRoot( );
if ( tpack == null ) {
tpack = classFile.getPackage( );
}
}
bindingDefinition.setFactoryLocation( tpack, rootFile );
}
} catch ( JiBXException ex ) {
LOG.error( "Unable to process binding " + url, ex );
}
}
}
private static ClassFile findMappedClass( BindingElement root ) {
ArrayList childs = root.topChildren( );
if ( childs != null ) {
// recursively search for modifiable mapped class
for ( int i = childs.size( ) - 1; i >= 0; i-- ) {
Object child = childs.get( i );
if ( child instanceof MappingElement ) {
// end scan if a real mapping is found
MappingElementBase map = ( MappingElementBase ) child;
ClassFile cf = map.getHandledClass( ).getClassFile( );
if ( !cf.isInterface( ) && cf.isModifiable( ) ) {
return cf;
}
} else if ( child instanceof IncludeElement ) {
// recurse on included binding
BindingElement bind = ( ( IncludeElement ) child ).getBinding( );
if ( bind != null ) {
ClassFile cf = findMappedClass( bind );
if ( cf != null ) {
return cf;
}
}
}
}
}
return null;
}
public void processBindings( ) throws JiBXException {
if ( !this.isBound ) {
for ( BindingDefinition binding : this.bindings ) {
binding.generateCode( System.getProperty( "euca.debug.binding.compile" ) != null, System.getProperty( "euca.debug.binding.compile" ) != null );
}
ClassFile[][] lists = MungedClass.fixDispositions( );
for ( BindingDefinition binding : this.bindings ) {
binding.addClassList( lists[0], lists[1] );
}
for ( int i = 0; i < lists[0].length; i++ ) {
ClassFile clas = lists[0][i];
this.classMap.put( clas.getName( ), clas );
}
this.isBound = true;
}
}
protected boolean isBoundClass( String name ) {
// if ( !this.isBound ) {
// try {
// this.processBindings( );
// } catch ( JiBXException e ) {
// e.printStackTrace( );
// }
// }
return this.classMap.containsKey( name );
}
protected Class findClass( String name ) throws ClassNotFoundException {
if ( isBoundClass( name ) ) {
try {
ClassFile clas = ( ClassFile ) this.classMap.get( name );
ByteArrayOutputStream bos = new ByteArrayOutputStream( );
clas.writeFile( bos );
byte[] bytes = bos.toByteArray( );
return defineClass( name, bytes, 0, bytes.length );
} catch ( IOException e ) {
throw new ClassNotFoundException( "Unable to load modified class " + name );
}
} else {
return super.findClass( name );
}
}
}