/******************************************************************************* * Copyright (c) 2010-present Sonatype, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Stuart McCulloch (Sonatype, Inc.) - initial API and implementation *******************************************************************************/ package org.eclipse.sisu.space; import java.lang.reflect.Modifier; import java.security.AccessController; import java.security.PrivilegedAction; import org.eclipse.sisu.inject.DeferredClass; import org.eclipse.sisu.space.asm.ClassWriter; import org.eclipse.sisu.space.asm.MethodVisitor; import org.eclipse.sisu.space.asm.Opcodes; /** * {@link ClassSpace} that can create multiple (deferred) copies of the same implementation type. */ public final class CloningClassSpace extends URLClassSpace { // ---------------------------------------------------------------------- // Constants // ---------------------------------------------------------------------- private static final String CLONE_MARKER = "$__sisu"; // ---------------------------------------------------------------------- // Implementation fields // ---------------------------------------------------------------------- private int cloneCount; // ---------------------------------------------------------------------- // Constructors // ---------------------------------------------------------------------- public CloningClassSpace( final ClassSpace parent ) { super( AccessController.doPrivileged( new PrivilegedAction<ClassLoader>() { public ClassLoader run() { return new CloningClassLoader( parent ); } } ), null ); } // ---------------------------------------------------------------------- // Public methods // ---------------------------------------------------------------------- public DeferredClass<?> cloneClass( final String name ) { final StringBuilder buf = new StringBuilder(); if ( name.startsWith( "java" ) ) { buf.append( '$' ); } return deferLoadClass( buf.append( name ).append( CLONE_MARKER ).append( ++cloneCount ).toString() ); } public static String originalName( final String proxyName ) { final int cloneMarker = proxyName.lastIndexOf( CLONE_MARKER ); if ( cloneMarker < 0 ) { return proxyName; } for ( int i = cloneMarker + CLONE_MARKER.length(), end = proxyName.length(); i < end; i++ ) { final char c = proxyName.charAt( i ); if ( c < '0' || c > '9' ) { return proxyName; // belongs to someone else, don't truncate the name } } return proxyName.substring( '$' == proxyName.charAt( 0 ) ? 1 : 0, cloneMarker ); } // ---------------------------------------------------------------------- // Implementation types // ---------------------------------------------------------------------- /** * {@link ClassLoader} that can define multiple copies of the same implementation type. */ private static final class CloningClassLoader extends ClassLoader { private final ClassSpace parent; CloningClassLoader( final ClassSpace parent ) { this.parent = parent; } @Override public String toString() { return parent.toString(); } @Override protected synchronized Class<?> loadClass( final String name, final boolean resolve ) throws ClassNotFoundException { if ( !name.contains( CLONE_MARKER ) ) { try { return parent.loadClass( name ); } catch ( final TypeNotPresentException e ) { throw new ClassNotFoundException( name ); } } return super.loadClass( name, resolve ); } @Override protected Class<?> findClass( final String name ) throws ClassNotFoundException { final String proxyName = name.replace( '.', '/' ); final String superName = originalName( proxyName ); if ( superName.equals( proxyName ) ) { throw new ClassNotFoundException( name ); } final ClassWriter cw = new ClassWriter( 0 ); cw.visit( Opcodes.V1_6, Modifier.PUBLIC, proxyName, null, superName, null ); final MethodVisitor mv = cw.visitMethod( Modifier.PUBLIC, "<init>", "()V", null, null ); mv.visitCode(); mv.visitVarInsn( Opcodes.ALOAD, 0 ); mv.visitMethodInsn( Opcodes.INVOKESPECIAL, superName, "<init>", "()V", false ); mv.visitInsn( Opcodes.RETURN ); mv.visitMaxs( 1, 1 ); mv.visitEnd(); cw.visitEnd(); final byte[] buf = cw.toByteArray(); return defineClass( name, buf, 0, buf.length ); } } }