001package org.apache.maven.exception; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.IOException; 023import java.net.ConnectException; 024import java.net.UnknownHostException; 025import java.util.ArrayList; 026import java.util.List; 027 028import org.apache.maven.lifecycle.LifecycleExecutionException; 029import org.apache.maven.model.building.ModelProblem; 030import org.apache.maven.model.building.ModelProblemUtils; 031import org.apache.maven.plugin.AbstractMojoExecutionException; 032import org.apache.maven.plugin.MojoExecutionException; 033import org.apache.maven.plugin.MojoFailureException; 034import org.apache.maven.plugin.PluginContainerException; 035import org.apache.maven.plugin.PluginExecutionException; 036import org.apache.maven.project.ProjectBuildingException; 037import org.apache.maven.project.ProjectBuildingResult; 038import org.codehaus.plexus.component.annotations.Component; 039import org.codehaus.plexus.util.StringUtils; 040 041/* 042 043- test projects for each of these 044- how to categorize the problems so that the id of the problem can be match to a page with descriptive help and the test 045 project 046- nice little sample projects that could be run in the core as well as integration tests 047 048All Possible Errors 049- invalid lifecycle phase (maybe same as bad CLI param, though you were talking about embedder too) 050- <module> specified is not found 051- malformed settings 052- malformed POM 053- local repository not writable 054- remote repositories not available 055- artifact metadata missing 056- extension metadata missing 057- extension artifact missing 058- artifact metadata retrieval problem 059- version range violation 060- circular dependency 061- artifact missing 062- artifact retrieval exception 063- md5 checksum doesn't match for local artifact, need to redownload this 064- POM doesn't exist for a goal that requires one 065- parent POM missing (in both the repository + relative path) 066- component not found 067 068Plugins: 069- plugin metadata missing 070- plugin metadata retrieval problem 071- plugin artifact missing 072- plugin artifact retrieval problem 073- plugin dependency metadata missing 074- plugin dependency metadata retrieval problem 075- plugin configuration problem 076- plugin execution failure due to something that is know to possibly go wrong (like compilation failure) 077- plugin execution error due to something that is not expected to go wrong (the compiler executable missing) 078- asking to use a plugin for which you do not have a version defined - tools to easily select versions 079- goal not found in a plugin (probably could list the ones that are) 080 081 */ 082 083//PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, CycleDetectedInPluginGraphException; 084 085@Component( role = ExceptionHandler.class ) 086public class DefaultExceptionHandler 087 implements ExceptionHandler 088{ 089 090 public ExceptionSummary handleException( Throwable exception ) 091 { 092 return handle( "", exception ); 093 } 094 095 private ExceptionSummary handle( String message, Throwable exception ) 096 { 097 String reference = getReference( exception ); 098 099 List<ExceptionSummary> children = null; 100 101 if ( exception instanceof ProjectBuildingException ) 102 { 103 List<ProjectBuildingResult> results = ( (ProjectBuildingException) exception ).getResults(); 104 105 children = new ArrayList<ExceptionSummary>(); 106 107 for ( ProjectBuildingResult result : results ) 108 { 109 ExceptionSummary child = handle( result ); 110 if ( child != null ) 111 { 112 children.add( child ); 113 } 114 } 115 116 message = "The build could not read " + children.size() + " project" + ( children.size() == 1 ? "" : "s" ); 117 } 118 else 119 { 120 message = getMessage( message, exception ); 121 } 122 123 return new ExceptionSummary( exception, message, reference, children ); 124 } 125 126 private ExceptionSummary handle( ProjectBuildingResult result ) 127 { 128 List<ExceptionSummary> children = new ArrayList<ExceptionSummary>(); 129 130 for ( ModelProblem problem : result.getProblems() ) 131 { 132 ExceptionSummary child = handle( problem, result.getProjectId() ); 133 if ( child != null ) 134 { 135 children.add( child ); 136 } 137 } 138 139 if ( children.isEmpty() ) 140 { 141 return null; 142 } 143 144 String message = 145 "\nThe project " + result.getProjectId() + " (" + result.getPomFile() + ") has " 146 + children.size() + " error" + ( children.size() == 1 ? "" : "s" ); 147 148 return new ExceptionSummary( null, message, null, children ); 149 } 150 151 private ExceptionSummary handle( ModelProblem problem, String projectId ) 152 { 153 if ( ModelProblem.Severity.ERROR.compareTo( problem.getSeverity() ) >= 0 ) 154 { 155 String message = problem.getMessage(); 156 157 String location = ModelProblemUtils.formatLocation( problem, projectId ); 158 159 if ( StringUtils.isNotEmpty( location ) ) 160 { 161 message += " @ " + location; 162 } 163 164 return handle( message, problem.getException() ); 165 } 166 else 167 { 168 return null; 169 } 170 } 171 172 private String getReference( Throwable exception ) 173 { 174 String reference = ""; 175 176 if ( exception != null ) 177 { 178 if ( exception instanceof MojoExecutionException ) 179 { 180 reference = MojoExecutionException.class.getSimpleName(); 181 182 Throwable cause = exception.getCause(); 183 if ( cause instanceof IOException ) 184 { 185 cause = cause.getCause(); 186 if ( cause instanceof ConnectException ) 187 { 188 reference = ConnectException.class.getSimpleName(); 189 } 190 } 191 } 192 else if ( exception instanceof MojoFailureException ) 193 { 194 reference = MojoFailureException.class.getSimpleName(); 195 } 196 else if ( exception instanceof LinkageError ) 197 { 198 reference = LinkageError.class.getSimpleName(); 199 } 200 else if ( exception instanceof PluginExecutionException ) 201 { 202 Throwable cause = exception.getCause(); 203 204 if ( cause instanceof PluginContainerException ) 205 { 206 Throwable cause2 = cause.getCause(); 207 208 if ( cause2 instanceof NoClassDefFoundError 209 && cause2.getMessage().contains( "org/sonatype/aether/" ) ) 210 { 211 reference = "AetherClassNotFound"; 212 } 213 } 214 215 if ( StringUtils.isEmpty( reference ) ) 216 { 217 reference = getReference( cause ); 218 } 219 220 if ( StringUtils.isEmpty( reference ) ) 221 { 222 reference = exception.getClass().getSimpleName(); 223 } 224 } 225 else if ( exception instanceof LifecycleExecutionException ) 226 { 227 reference = getReference( exception.getCause() ); 228 } 229 else if ( isNoteworthyException( exception ) ) 230 { 231 reference = exception.getClass().getSimpleName(); 232 } 233 } 234 235 if ( StringUtils.isNotEmpty( reference ) && !reference.startsWith( "http:" ) ) 236 { 237 reference = "http://cwiki.apache.org/confluence/display/MAVEN/" + reference; 238 } 239 240 return reference; 241 } 242 243 private boolean isNoteworthyException( Throwable exception ) 244 { 245 if ( exception == null ) 246 { 247 return false; 248 } 249 else if ( exception instanceof Error ) 250 { 251 return true; 252 } 253 else if ( exception instanceof RuntimeException ) 254 { 255 return false; 256 } 257 else if ( exception.getClass().getName().startsWith( "java" ) ) 258 { 259 return false; 260 } 261 return true; 262 } 263 264 private String getMessage( String message, Throwable exception ) 265 { 266 String fullMessage = ( message != null ) ? message : ""; 267 268 for ( Throwable t = exception; t != null; t = t.getCause() ) 269 { 270 String exceptionMessage = t.getMessage(); 271 272 if ( t instanceof AbstractMojoExecutionException ) 273 { 274 String longMessage = ( (AbstractMojoExecutionException) t ).getLongMessage(); 275 if ( StringUtils.isNotEmpty( longMessage ) ) 276 { 277 if ( StringUtils.isEmpty( exceptionMessage ) || longMessage.contains( exceptionMessage ) ) 278 { 279 exceptionMessage = longMessage; 280 } 281 else if ( !exceptionMessage.contains( longMessage ) ) 282 { 283 exceptionMessage = join( exceptionMessage, '\n' + longMessage ); 284 } 285 } 286 } 287 288 if ( StringUtils.isEmpty( exceptionMessage ) ) 289 { 290 exceptionMessage = t.getClass().getSimpleName(); 291 } 292 293 if ( t instanceof UnknownHostException && !fullMessage.contains( "host" ) ) 294 { 295 fullMessage = join( fullMessage, "Unknown host " + exceptionMessage ); 296 } 297 else if ( !fullMessage.contains( exceptionMessage ) ) 298 { 299 fullMessage = join( fullMessage, exceptionMessage ); 300 } 301 } 302 303 return fullMessage.trim(); 304 } 305 306 private String join( String message1, String message2 ) 307 { 308 String message = ""; 309 310 if ( StringUtils.isNotEmpty( message1 ) ) 311 { 312 message = message1.trim(); 313 } 314 315 if ( StringUtils.isNotEmpty( message2 ) ) 316 { 317 if ( StringUtils.isNotEmpty( message ) ) 318 { 319 if ( message.endsWith( "." ) || message.endsWith( "!" ) || message.endsWith( ":" ) ) 320 { 321 message += " "; 322 } 323 else 324 { 325 message += ": "; 326 } 327 } 328 329 message += message2; 330 } 331 332 return message; 333 } 334 335}