o Optimized the cache around the hotspot in interpolation somewhat

git-svn-id: https://svn.apache.org/repos/asf/maven/maven-3/trunk@1376085 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Kristian Rosenvold 2012-08-22 14:55:33 +00:00
parent 31285e3b68
commit 100bbd031b
1 changed files with 338 additions and 215 deletions

View File

@ -22,7 +22,9 @@ package org.apache.maven.model.interpolation;
import org.apache.maven.model.Model; import org.apache.maven.model.Model;
import org.apache.maven.model.building.ModelBuildingRequest; import org.apache.maven.model.building.ModelBuildingRequest;
import org.apache.maven.model.building.ModelProblem.Severity; import org.apache.maven.model.building.ModelProblem.Severity;
import org.apache.maven.model.building.ModelProblem.Version;
import org.apache.maven.model.building.ModelProblemCollector; import org.apache.maven.model.building.ModelProblemCollector;
import org.apache.maven.model.building.ModelProblemCollectorRequest;
import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.interpolation.InterpolationPostProcessor; import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
import org.codehaus.plexus.interpolation.Interpolator; import org.codehaus.plexus.interpolation.Interpolator;
@ -41,19 +43,16 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.apache.maven.model.building.ModelProblem;
import org.apache.maven.model.building.ModelProblem.Version;
import org.apache.maven.model.building.ModelProblemCollectorRequest;
@Component( role = ModelInterpolator.class ) @Component( role = ModelInterpolator.class )
public class StringSearchModelInterpolator public class StringSearchModelInterpolator
extends AbstractStringBasedModelInterpolator extends AbstractStringBasedModelInterpolator
{ {
private static final Map<Class<?>, Field[]> fieldsByClass = private static final Map<Class<?>, InterpolateObjectAction.CacheItem> cachedEntries =
new ConcurrentHashMap<Class<?>, Field[]>( 80, 0.75f, 2 ); // Empirical data from 3.x, actual =40 new ConcurrentHashMap<Class<?>, InterpolateObjectAction.CacheItem>( 80, 0.75f, 2 );
private static final Map<Class<?>, Boolean> fieldIsPrimitiveByClass = // Empirical data from 3.x, actual =40
new ConcurrentHashMap<Class<?>, Boolean>( 62, 0.75f, 2 ); // Empirical data from 3.x, actual 31
public Model interpolateModel( Model model, File projectDir, ModelBuildingRequest config, public Model interpolateModel( Model model, File projectDir, ModelBuildingRequest config,
ModelProblemCollector problems ) ModelProblemCollector problems )
@ -69,8 +68,8 @@ public class StringSearchModelInterpolator
try try
{ {
List<? extends ValueSource> valueSources = createValueSources( model, projectDir, config, problems ); List<? extends ValueSource> valueSources = createValueSources( model, projectDir, config, problems );
List<? extends InterpolationPostProcessor> postProcessors = createPostProcessors( model, projectDir, List<? extends InterpolationPostProcessor> postProcessors =
config ); createPostProcessors( model, projectDir, config );
InterpolateObjectAction action = InterpolateObjectAction action =
new InterpolateObjectAction( obj, valueSources, postProcessors, this, problems ); new InterpolateObjectAction( obj, valueSources, postProcessors, this, problems );
@ -96,9 +95,13 @@ public class StringSearchModelInterpolator
{ {
private final LinkedList<Object> interpolationTargets; private final LinkedList<Object> interpolationTargets;
private final StringSearchModelInterpolator modelInterpolator; private final StringSearchModelInterpolator modelInterpolator;
private final List<? extends ValueSource> valueSources; private final List<? extends ValueSource> valueSources;
private final List<? extends InterpolationPostProcessor> postProcessors; private final List<? extends InterpolationPostProcessor> postProcessors;
private final ModelProblemCollector problems; private final ModelProblemCollector problems;
public InterpolateObjectAction( Object target, List<? extends ValueSource> valueSources, public InterpolateObjectAction( Object target, List<? extends ValueSource> valueSources,
@ -129,6 +132,12 @@ public class StringSearchModelInterpolator
return null; return null;
} }
private String interpolate( String value )
{
return modelInterpolator.interpolateInternal( value, valueSources, postProcessors, problems );
}
private void traverseObjectWithParents( Class<?> cls, Object target ) private void traverseObjectWithParents( Class<?> cls, Object target )
{ {
if ( cls == null ) if ( cls == null )
@ -136,73 +145,189 @@ public class StringSearchModelInterpolator
return; return;
} }
if ( cls.isArray() ) CacheItem cacheEntry = getCacheEntry( cls );
if ( cacheEntry.isArray() )
{ {
evaluateArray( target ); evaluateArray( target, this );
} }
else if ( isQualifiedForInterpolation( cls ) ) else if ( cacheEntry.isQualifiedForInterpolation )
{ {
for ( Field currentField : getFields( cls ) ) cacheEntry.interpolate( target, problems, this );
{
Class<?> type = currentField.getType();
if ( isQualifiedForInterpolation( currentField, type ) )
{
synchronized ( currentField )
{
interpolateField( cls, target, currentField, type );
}
}
}
traverseObjectWithParents( cls.getSuperclass(), target ); traverseObjectWithParents( cls.getSuperclass(), target );
} }
} }
private void interpolateField( Class<?> cls, Object target, Field field, Class<?> type )
private CacheItem getCacheEntry( Class<?> cls )
{
CacheItem cacheItem = cachedEntries.get( cls );
if ( cacheItem == null )
{
cacheItem = new CacheItem( cls );
cachedEntries.put( cls, cacheItem );
}
return cacheItem;
}
private boolean isQualifiedForInterpolation( Class<?> cls )
{
return !cls.getName().startsWith( "java" );
}
private boolean isQualifiedForInterpolation( Field field, Class<?> fieldType )
{
if ( Map.class.equals( fieldType ) && "locations".equals( field.getName() ) )
{
return false;
}
if ( fieldType.isPrimitive() )
{
return false;
}
return !"parent".equals( field.getName() );
}
private static void evaluateArray( Object target, InterpolateObjectAction ctx )
{
int len = Array.getLength( target );
for ( int i = 0; i < len; i++ )
{
Object value = Array.get( target, i );
if ( value != null )
{
if ( String.class == value.getClass() )
{
String interpolated = ctx.interpolate( (String) value );
if ( !interpolated.equals( value ) )
{
Array.set( target, i, interpolated );
}
}
else
{
ctx.interpolationTargets.add( value );
}
}
}
}
private static class CacheItem
{
private final boolean isArray;
private final boolean isQualifiedForInterpolation;
private final CacheField[] fields;
private boolean isQualifiedForInterpolation( Class<?> cls )
{
return !cls.getName().startsWith( "java" );
}
private boolean isQualifiedForInterpolation( Field field, Class<?> fieldType )
{
if ( Map.class.equals( fieldType ) && "locations".equals( field.getName() ) )
{
return false;
}
if ( fieldType.isPrimitive() )
{
return false;
}
return !"parent".equals( field.getName() );
}
CacheItem( Class clazz )
{
this.isQualifiedForInterpolation = isQualifiedForInterpolation( clazz );
this.isArray = clazz.isArray();
List<CacheField> fields = new ArrayList<CacheField>();
for ( Field currentField : clazz.getDeclaredFields() )
{
Class<?> type = currentField.getType();
if ( isQualifiedForInterpolation( currentField, type ) )
{
if ( String.class == type )
{
if ( !Modifier.isFinal( currentField.getModifiers() ) )
{
fields.add( new StringField( currentField ) );
}
}
else if ( Collection.class.isAssignableFrom( type ) )
{
fields.add( new CollectionField( currentField ) );
}
else if ( Map.class.isAssignableFrom( type ) )
{
fields.add( new MapField( currentField ) );
}
else
{
fields.add( new ObjectField( currentField ) );
}
}
}
this.fields = fields.toArray( new CacheField[fields.size()] );
}
public void interpolate( Object target, ModelProblemCollector problems,
InterpolateObjectAction interpolateObjectAction )
{
for ( CacheField field : fields )
{
field.interpolate( target, problems, interpolateObjectAction );
}
}
public boolean isArray()
{
return isArray;
}
}
static abstract class CacheField
{
protected final Field field;
CacheField( Field field )
{
this.field = field;
}
void interpolate( Object target, ModelProblemCollector problems,
InterpolateObjectAction interpolateObjectAction )
{
synchronized ( field )
{ {
boolean isAccessible = field.isAccessible(); boolean isAccessible = field.isAccessible();
field.setAccessible( true ); field.setAccessible( true );
try try
{ {
if ( String.class == type ) doInterpolate( target, interpolateObjectAction );
{
interpolateStringField( target, field );
}
else if ( Collection.class.isAssignableFrom( type ) )
{
interpolateCollectionField( target, field );
}
else if ( Map.class.isAssignableFrom( type ) )
{
interpolateMapField( target, field );
}
else
{
Object value = field.get( target );
if ( value != null )
{
if ( field.getType().isArray() )
{
evaluateArray( value );
}
else
{
interpolationTargets.add( value );
}
}
}
} }
catch ( IllegalArgumentException e ) catch ( IllegalArgumentException e )
{ {
problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE) interpolateObjectAction.problems.add(
.setMessage( "Failed to interpolate field3: " + field + " on class: " + cls.getName()) new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ).setMessage(
.setException(e)); "Failed to interpolate field3: " + field + " on class: "
+ field.getType().getName() ).setException(
e ) ); // todo: Not entirely the same message
} }
catch ( IllegalAccessException e ) catch ( IllegalAccessException e )
{ {
problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE) interpolateObjectAction.problems.add(
.setMessage( "Failed to interpolate field4: " + field + " on class: " + cls.getName()) new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ).setMessage(
.setException(e)); "Failed to interpolate field4: " + field + " on class: "
+ field.getType().getName() ).setException( e ) );
} }
finally finally
{ {
@ -210,29 +335,53 @@ public class StringSearchModelInterpolator
} }
} }
private void interpolateStringField( Object target, Field field )
}
abstract void doInterpolate( Object target, InterpolateObjectAction ctx )
throws IllegalAccessException;
}
static final class StringField
extends CacheField
{
StringField( Field field )
{
super( field );
}
@Override
void doInterpolate( Object target, InterpolateObjectAction ctx )
throws IllegalAccessException throws IllegalAccessException
{ {
String value = (String) field.get( target ); String value = (String) field.get( target );
if ( value == null || Modifier.isFinal( field.getModifiers() ) ) if ( value == null )
{ {
return; return;
} }
String interpolated = String interpolated = ctx.interpolate( value );
modelInterpolator.interpolateInternal( value, valueSources, postProcessors, problems );
if ( !interpolated.equals( value ) ) if ( !interpolated.equals( value ) )
{ {
field.set( target, interpolated ); field.set( target, interpolated );
} }
} }
}
private void interpolateCollectionField( Object target, Field field ) static final class CollectionField
extends CacheField
{
CollectionField( Field field )
{
super( field );
}
@Override
void doInterpolate( Object target, InterpolateObjectAction ctx )
throws IllegalAccessException throws IllegalAccessException
{ {
@SuppressWarnings( "unchecked" ) @SuppressWarnings( "unchecked" ) Collection<Object> c = (Collection<Object>) field.get( target );
Collection<Object> c = (Collection<Object>) field.get( target );
if ( c == null || c.isEmpty() ) if ( c == null || c.isEmpty() )
{ {
return; return;
@ -257,8 +406,7 @@ public class StringSearchModelInterpolator
} }
else if ( String.class == value.getClass() ) else if ( String.class == value.getClass() )
{ {
String interpolated = String interpolated = ctx.interpolate( (String) value );
modelInterpolator.interpolateInternal( (String) value, valueSources, postProcessors, problems );
if ( !interpolated.equals( value ) ) if ( !interpolated.equals( value ) )
{ {
@ -274,21 +422,30 @@ public class StringSearchModelInterpolator
c.add( value ); c.add( value );
if ( value.getClass().isArray() ) if ( value.getClass().isArray() )
{ {
evaluateArray( value ); evaluateArray( value, ctx );
} }
else else
{ {
interpolationTargets.add( value ); ctx.interpolationTargets.add( value );
}
} }
} }
} }
} }
private void interpolateMapField( Object target, Field field ) static final class MapField
extends CacheField
{
MapField( Field field )
{
super( field );
}
@Override
void doInterpolate( Object target, InterpolateObjectAction ctx )
throws IllegalAccessException throws IllegalAccessException
{ {
@SuppressWarnings( "unchecked" ) @SuppressWarnings( "unchecked" ) Map<Object, Object> m = (Map<Object, Object>) field.get( target );
Map<Object, Object> m = (Map<Object, Object>) field.get( target );
if ( m == null || m.isEmpty() ) if ( m == null || m.isEmpty() )
{ {
return; return;
@ -305,8 +462,7 @@ public class StringSearchModelInterpolator
if ( String.class == value.getClass() ) if ( String.class == value.getClass() )
{ {
String interpolated = String interpolated = ctx.interpolate( (String) value );
modelInterpolator.interpolateInternal( (String) value, valueSources, postProcessors, problems );
if ( !interpolated.equals( value ) ) if ( !interpolated.equals( value ) )
{ {
@ -322,79 +478,46 @@ public class StringSearchModelInterpolator
} }
else if ( value.getClass().isArray() ) else if ( value.getClass().isArray() )
{ {
evaluateArray( value ); evaluateArray( value, ctx );
} }
else else
{ {
interpolationTargets.add( value ); ctx.interpolationTargets.add( value );
}
} }
} }
} }
private Field[] getFields( Class<?> cls ) static final class ObjectField
extends CacheField
{ {
Field[] fields = fieldsByClass.get( cls ); private final boolean isArray;
if ( fields == null )
ObjectField( Field field )
{ {
fields = cls.getDeclaredFields(); super( field );
fieldsByClass.put( cls, fields ); this.isArray = field.getType().isArray();
}
return fields;
} }
private boolean isQualifiedForInterpolation( Class<?> cls ) @Override
void doInterpolate( Object target, InterpolateObjectAction ctx )
throws IllegalAccessException
{ {
return !cls.getName().startsWith( "java" ); Object value = field.get( target );
}
private boolean isQualifiedForInterpolation( Field field, Class<?> fieldType )
{
if ( Map.class.equals( fieldType ) && "locations".equals( field.getName() ) )
{
return false;
}
Boolean primitive = fieldIsPrimitiveByClass.get( fieldType );
if ( primitive == null )
{
primitive = fieldType.isPrimitive();
fieldIsPrimitiveByClass.put( fieldType, primitive );
}
if ( primitive )
{
return false;
}
return !"parent".equals( field.getName() );
}
private void evaluateArray( Object target )
{
int len = Array.getLength( target );
for ( int i = 0; i < len; i++ )
{
Object value = Array.get( target, i );
if ( value != null ) if ( value != null )
{ {
if ( String.class == value.getClass() ) if ( isArray )
{ {
String interpolated = evaluateArray( value, ctx );
modelInterpolator.interpolateInternal( (String) value, valueSources, postProcessors,
problems );
if ( !interpolated.equals( value ) )
{
Array.set( target, i, interpolated );
}
} }
else else
{ {
interpolationTargets.add( value ); ctx.interpolationTargets.add( value );
} }
} }
} }
} }
} }
} }