o Revised exception handling of model builder to collect as much as possible in ModelProblem instances that are stored in the ModelBuildingException when it eventually bails out. This should enable IDE integrators to provide more extensive error feedback.

git-svn-id: https://svn.apache.org/repos/asf/maven/components/trunk@782282 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Benjamin Bentmann 2009-06-06 17:28:10 +00:00
parent 8eedb83477
commit b5e672f92b
8 changed files with 225 additions and 311 deletions

View File

@ -110,17 +110,19 @@ public class DefaultModelBuilder
{
DefaultModelBuildingResult result = new DefaultModelBuildingResult();
List<ModelProblem> problems = new ArrayList<ModelProblem>();
ProfileActivationContext profileActivationContext = getProfileActivationContext( request );
List<Profile> activeExternalProfiles = getActiveExternalProfiles( request, profileActivationContext );
List<Profile> activeExternalProfiles = getActiveExternalProfiles( request, profileActivationContext, problems );
Model model = readModel( modelSource, request );
Model model = readModel( modelSource, request, problems );
model.setPomFile( pomFile );
List<Model> rawModels = new ArrayList<Model>();
List<Model> resultModels = new ArrayList<Model>();
for ( Model current = model; current != null; current = readParent( current, request ) )
for ( Model current = model; current != null; current = readParent( current, request, problems ) )
{
Model resultModel = current;
resultModels.add( resultModel );
@ -130,7 +132,8 @@ public class DefaultModelBuilder
modelNormalizer.mergeDuplicates( resultModel, request );
List<Profile> activeProjectProfiles = getActiveProjectProfiles( rawModel, profileActivationContext );
List<Profile> activeProjectProfiles =
getActiveProjectProfiles( rawModel, profileActivationContext, problems );
List<Profile> activeProfiles = activeProjectProfiles;
if ( current == model )
@ -147,7 +150,7 @@ public class DefaultModelBuilder
result.setActiveProfiles( rawModel, activeProfiles );
configureResolver( request.getModelResolver(), resultModel );
configureResolver( request.getModelResolver(), resultModel, problems );
}
Model superModel = getSuperModel();
@ -160,7 +163,7 @@ public class DefaultModelBuilder
Model resultModel = resultModels.get( 0 );
resultModel = interpolateModel( resultModel, request );
resultModel = interpolateModel( resultModel, request, problems );
resultModels.set( 0, resultModel );
modelPathTranslator.alignToBaseDirectory( resultModel, resultModel.getProjectDirectory(), request );
@ -177,7 +180,12 @@ public class DefaultModelBuilder
pluginConfigurationExpander.expandPluginConfiguration( resultModel, request );
}
validateModel( resultModel, false, request );
validateModel( resultModel, false, request, problems );
if ( !problems.isEmpty() )
{
throw new ModelBuildingException( problems );
}
result.setEffectiveModel( resultModel );
@ -193,7 +201,7 @@ public class DefaultModelBuilder
return context;
}
private Model readModel( ModelSource modelSource, ModelBuildingRequest request )
private Model readModel( ModelSource modelSource, ModelBuildingRequest request, List<ModelProblem> problems )
throws ModelBuildingException
{
Model model;
@ -207,21 +215,23 @@ public class DefaultModelBuilder
}
catch ( ModelParseException e )
{
throw new UnparseableModelException( "Failed to parse POM " + modelSource.getLocation() + ": "
+ e.getMessage(), e.getLineNumber(), e.getColumnNumber(), e );
problems.add( new ModelProblem( "Non-parseable POM " + modelSource.getLocation() + ": " + e.getMessage(),
modelSource.getLocation(), e ) );
throw new ModelBuildingException( problems );
}
catch ( IOException e )
{
throw new UnparseableModelException( "Failed to read POM " + modelSource.getLocation(), -1, -1, e );
problems.add( new ModelProblem( "Non-readable POM " + modelSource.getLocation() + ": " + e.getMessage(),
modelSource.getLocation(), e ) );
throw new ModelBuildingException( problems );
}
validateModel( model, true, request );
validateModel( model, true, request, problems );
return model;
}
private void validateModel( Model model, boolean raw, ModelBuildingRequest request )
throws ModelBuildingException
private void validateModel( Model model, boolean raw, ModelBuildingRequest request, List<ModelProblem> problems )
{
ModelValidationResult result;
@ -236,12 +246,17 @@ public class DefaultModelBuilder
if ( result.getMessageCount() > 0 )
{
throw new InvalidModelException( "Failed to validate POM " + toSourceHint( model ), result );
String source = toSourceHint( model );
for ( int i = 0; i < result.getMessageCount(); i++ )
{
problems.add( new ModelProblem( "Invalid POM " + source + ": " + result.getMessage( i ), source ) );
}
}
}
private List<Profile> getActiveExternalProfiles( ModelBuildingRequest request, ProfileActivationContext context )
throws ModelBuildingException
private List<Profile> getActiveExternalProfiles( ModelBuildingRequest request, ProfileActivationContext context,
List<ModelProblem> problems )
{
try
{
@ -249,12 +264,16 @@ public class DefaultModelBuilder
}
catch ( ProfileActivationException e )
{
throw new InvalidProfileException( "Failed to determine activation status of external profile "
+ e.getProfile(), e.getProfile(), e );
problems.add( new ModelProblem( "Invalid activation condition for external profile "
+ e.getProfile().getId() + ": " + e.getMessage(), "(external profiles)", e ) );
// FIXME: Update profile selector to integrate better with the problem reporting
return new ArrayList<Profile>();
}
}
private List<Profile> getActiveProjectProfiles( Model model, ProfileActivationContext context )
private List<Profile> getActiveProjectProfiles( Model model, ProfileActivationContext context,
List<ModelProblem> problems )
throws ModelBuildingException
{
try
@ -263,13 +282,16 @@ public class DefaultModelBuilder
}
catch ( ProfileActivationException e )
{
throw new InvalidProfileException( "Failed to determine activation status of project profile "
+ e.getProfile() + " for POM " + toSourceHint( model ), e.getProfile(), e );
problems.add( new ModelProblem( "Invalid activation condition for project profile "
+ e.getProfile().getId() + " in POM " + toSourceHint( model ) + ": " + e.getMessage(),
toSourceHint( model ), e ) );
// FIXME: Update profile selector to integrate better with the problem reporting
return new ArrayList<Profile>();
}
}
private void configureResolver( ModelResolver modelResolver, Model model )
throws ModelBuildingException
private void configureResolver( ModelResolver modelResolver, Model model, List<ModelProblem> problems )
{
if ( modelResolver == null )
{
@ -284,8 +306,8 @@ public class DefaultModelBuilder
}
catch ( InvalidRepositoryException e )
{
throw new InvalidModelException( "Failed to validate repository " + repository.getId() + " for POM "
+ toSourceHint( model ), e );
problems.add( new ModelProblem( "Invalid repository " + repository.getId() + " in POM "
+ toSourceHint( model ) + ": " + e.getMessage(), toSourceHint( model ), e ) );
}
}
}
@ -300,8 +322,7 @@ public class DefaultModelBuilder
}
}
private Model interpolateModel( Model model, ModelBuildingRequest request )
throws ModelBuildingException
private Model interpolateModel( Model model, ModelBuildingRequest request, List<ModelProblem> problems )
{
try
{
@ -311,11 +332,14 @@ public class DefaultModelBuilder
}
catch ( ModelInterpolationException e )
{
throw new ModelBuildingException( "Failed to interpolate model " + toSourceHint( model ), e );
problems.add( new ModelProblem( "Invalid expression in POM " + toSourceHint( model ) + ": "
+ e.getMessage(), toSourceHint( model ), e ) );
return model;
}
}
private Model readParent( Model childModel, ModelBuildingRequest request )
private Model readParent( Model childModel, ModelBuildingRequest request, List<ModelProblem> problems )
throws ModelBuildingException
{
Model parentModel;
@ -324,11 +348,11 @@ public class DefaultModelBuilder
if ( parent != null )
{
parentModel = readParentLocally( childModel, request );
parentModel = readParentLocally( childModel, request, problems );
if ( parentModel == null )
{
parentModel = readParentExternally( childModel, request );
parentModel = readParentExternally( childModel, request, problems );
}
}
else
@ -339,7 +363,7 @@ public class DefaultModelBuilder
return parentModel;
}
private Model readParentLocally( Model childModel, ModelBuildingRequest request )
private Model readParentLocally( Model childModel, ModelBuildingRequest request, List<ModelProblem> problems )
throws ModelBuildingException
{
File projectDirectory = childModel.getProjectDirectory();
@ -360,7 +384,7 @@ public class DefaultModelBuilder
return null;
}
Model candidateModel = readModel( new FileModelSource( pomFile ), request );
Model candidateModel = readModel( new FileModelSource( pomFile ), request, problems );
candidateModel.setPomFile( pomFile );
String groupId = candidateModel.getGroupId();
@ -391,7 +415,7 @@ public class DefaultModelBuilder
return candidateModel;
}
private Model readParentExternally( Model childModel, ModelBuildingRequest request )
private Model readParentExternally( Model childModel, ModelBuildingRequest request, List<ModelProblem> problems )
throws ModelBuildingException
{
Parent parent = childModel.getParent();
@ -400,9 +424,9 @@ public class DefaultModelBuilder
if ( modelResolver == null )
{
Exception e = new IllegalArgumentException( "No model resolver provided" );
throw new UnresolvableParentException( "Failed to resolve parent POM " + toId( parent ) + " for POM "
+ toSourceHint( childModel ), e );
problems.add( new ModelProblem( "Non-resolvable parent POM " + toId( parent ) + " for POM "
+ toSourceHint( childModel ) + ": " + "No model resolver provided", toSourceHint( childModel ) ) );
throw new ModelBuildingException( problems );
}
ModelSource modelSource;
@ -412,11 +436,12 @@ public class DefaultModelBuilder
}
catch ( UnresolvableModelException e )
{
throw new UnresolvableParentException( "Failed to resolve parent POM " + toId( parent ) + " for POM "
+ toSourceHint( childModel ), e );
problems.add( new ModelProblem( "Non-resolvable parent POM " + toId( parent ) + " for POM "
+ toSourceHint( childModel ) + ": " + e.getMessage(), toSourceHint( childModel ), e ) );
throw new ModelBuildingException( problems );
}
return readModel( modelSource, request );
return readModel( modelSource, request, problems );
}
private Model getSuperModel()

View File

@ -1,73 +0,0 @@
package org.apache.maven.model;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.apache.maven.model.validation.ModelValidationResult;
/**
* Signals an error due to invalid or missing model values.
*
* @author Benjamin Bentmann
*/
public class InvalidModelException
extends ModelBuildingException
{
/**
* The validation result, can be {@code null}.
*/
private ModelValidationResult validationResult;
/**
* Creates a new exception with specified detail message and validation result.
*
* @param message The detail message, may be {@code null}.
* @param validationResult The validation result, may be {@code null}.
*/
public InvalidModelException( String message, ModelValidationResult validationResult )
{
super( message );
this.validationResult = validationResult;
}
/**
* Creates a new exception with specified detail message and cause.
*
* @param message The detail message, may be {@code null}.
* @param cause The cause, may be {@code null}.
*/
public InvalidModelException( String message, Throwable cause )
{
super( message, cause );
validationResult = new ModelValidationResult();
validationResult.addMessage( ( cause != null ) ? cause.getMessage() : message );
}
/**
* Gets the validation result.
*
* @return The validation result or {@code null} if unknown.
*/
public ModelValidationResult getValidationResult()
{
return validationResult;
}
}

View File

@ -1,59 +0,0 @@
package org.apache.maven.model;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Signals an error during profile activation.
*
* @author Benjamin Bentmann
*/
public class InvalidProfileException
extends ModelBuildingException
{
/**
* The profile which raised this error, can be {@code null}.
*/
private Profile profile;
/**
* Creates a new exception with specified detail message and cause for the given profile.
*
* @param message The detail message, may be {@code null}.
* @param profile The profile that caused the error, may be {@code null}.
* @param cause The cause, may be {@code null}.
*/
public InvalidProfileException( String message, Profile profile, Throwable cause )
{
super( message );
this.profile = profile;
}
/**
* Gets the profile that caused this error (if any).
*
* @return The profile that caused this error or {@code null} if not applicable.
*/
public Profile getProfile()
{
return profile;
}
}

View File

@ -19,6 +19,11 @@ package org.apache.maven.model;
* under the License.
*/
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
/**
* Signals an error during model building.
*
@ -28,25 +33,51 @@ public class ModelBuildingException
extends Exception
{
private List<ModelProblem> problems;
/**
* Creates a new exception with specified detail message.
* Creates a new exception with the specified problems.
*
* @param message The detail message, may be {@code null}.
* @param problems The problems that causes this exception, may be {@code null}.
*/
public ModelBuildingException( String message )
public ModelBuildingException( List<ModelProblem> problems )
{
super( message );
super( toMessage( problems ) );
this.problems = new ArrayList<ModelProblem>();
if ( problems != null )
{
this.problems.addAll( problems );
}
}
/**
* Creates a new exception with specified detail message and cause.
* Gets the problems that caused this exception.
*
* @param message The detail message, may be {@code null}.
* @param cause The cause, may be {@code null}.
* @return The problems that caused this exception, never {@code null}.
*/
public ModelBuildingException( String message, Throwable cause )
public List<ModelProblem> getProblems()
{
super( message, cause );
return this.problems;
}
private static String toMessage( List<ModelProblem> problems )
{
StringWriter buffer = new StringWriter( 1024 );
PrintWriter writer = new PrintWriter( buffer );
writer.print( problems.size() );
writer.print( ( problems.size() == 1 ) ? " problem was " : " problems were " );
writer.println( "encountered during construction of the effective model:" );
for ( ModelProblem problem : problems )
{
writer.print( "o " );
writer.println( problem.getMessage() );
}
return buffer.toString();
}
}

View File

@ -0,0 +1,118 @@
package org.apache.maven.model;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Describes a problem that was encountered during model building. A problem can either be an exception that was thrown
* or a simple string message. In addition, a problem carries a hint about its source, e.g. the POM file that could not
* be processed.
*
* @author Benjamin Bentmann
*/
public class ModelProblem
{
private String source;
private String message;
private Exception exception;
/**
* Creates a new problem with the specified message.
*
* @param message The message describing the problem, may be {@code null}.
* @param source A hint about the source of the problem, may be {@code null}.
*/
public ModelProblem( String message, String source )
{
this.message = message;
this.source = ( source != null ) ? source : "";
}
/**
* Creates a new problem with the specified message and exception.
*
* @param message The message describing the problem, may be {@code null}.
* @param source A hint about the source of the problem, may be {@code null}.
* @param exception The exception that caused this problem, may be {@code null}.
*/
public ModelProblem( String message, String source, Exception exception )
{
this.message = message;
this.source = ( source != null ) ? source : "";
this.exception = exception;
}
/**
* Gets the hint about the source of the problem. While the syntax of this hint is unspecified and depends on the
* creator of the problem, the general expectation is that the hint provides sufficient information to the user to
* track the problem back to its origin.
*
* @return The hint about the source of the problem, never {@code null}.
*/
public String getSource()
{
return source;
}
/**
* Gets the exception that caused this problem (if any).
*
* @return The exception that caused this problem or {@code null} if not applicable.
*/
public Exception getException()
{
return exception;
}
/**
* Gets the message that describes this problem.
*
* @return The message describing this problem, never {@code null}.
*/
public String getMessage()
{
String msg;
if ( message != null && message.length() > 0 )
{
msg = message;
}
else
{
msg = exception.getMessage();
if ( msg == null )
{
msg = "";
}
}
return msg;
}
@Override
public String toString()
{
return getSource() + ": " + getMessage();
}
}

View File

@ -1,76 +0,0 @@
package org.apache.maven.model;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Signals a failure to read/parse a POM file.
*
* @author Benjamin Bentmann
*/
public class UnparseableModelException
extends ModelBuildingException
{
/**
* The one-based index of the line containing the error.
*/
private int lineNumber;
/**
* The one-based index of the column containing the error.
*/
private int columnNumber;
/**
* Creates a new parser exception with the specified details.
*
* @param message The error message, may be {@code null}.
* @param lineNumber The one-based index of the line containing the error or {@code -1} if unknown.
* @param columnNumber The one-based index of the column containing the error or {@code -1} if unknown.
* @param cause The nested cause of this error, may be {@code null}.
*/
public UnparseableModelException( String message, int lineNumber, int columnNumber, Throwable cause )
{
super( message, cause );
this.lineNumber = lineNumber;
this.columnNumber = columnNumber;
}
/**
* Gets the one-based index of the line containing the error.
*
* @return The one-based index of the line containing the error or a non-positive value if unknown.
*/
public int getLineNumber()
{
return lineNumber;
}
/**
* Gets the one-based index of the column containing the error.
*
* @return The one-based index of the column containing the error or non-positive value if unknown.
*/
public int getColumnNumber()
{
return columnNumber;
}
}

View File

@ -1,52 +0,0 @@
package org.apache.maven.model;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Signals an error when resolving a child's parent model.
*
* @author Benjamin Bentmann
*/
public class UnresolvableParentException
extends ModelBuildingException
{
/**
* Creates a new exception with specified detail message and cause.
*
* @param message The detail message, may be {@code null}.
* @param cause The cause, may be {@code null}.
*/
public UnresolvableParentException( String message, Throwable cause )
{
super( message, cause );
}
/**
* Creates a new exception with specified detail message.
*
* @param message The detail message, may be {@code null}.
*/
public UnresolvableParentException( String message )
{
super( message );
}
}

View File

@ -31,7 +31,7 @@ public interface PathTranslator
/**
* Resolves the specified path against the given base directory. The resolved path will be absolute and uses the
* platform-specified file separator.
* platform-specific file separator.
*
* @param path The path to resolve, may be {@code null}.
* @param basedir The base directory to resolve relative paths against, may be {@code null}.