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, 084// CycleDetectedInPluginGraphException; 085 086@Component( role = ExceptionHandler.class ) 087public class DefaultExceptionHandler 088 implements ExceptionHandler 089{ 090 091 public ExceptionSummary handleException( Throwable exception ) 092 { 093 return handle( "", exception ); 094 } 095 096 private ExceptionSummary handle( String message, Throwable exception ) 097 { 098 String reference = getReference( exception ); 099 100 List<ExceptionSummary> children = null; 101 102 if ( exception instanceof ProjectBuildingException ) 103 { 104 List<ProjectBuildingResult> results = ( (ProjectBuildingException) exception ).getResults(); 105 106 children = new ArrayList<>(); 107 108 for ( ProjectBuildingResult result : results ) 109 { 110 ExceptionSummary child = handle( result ); 111 if ( child != null ) 112 { 113 children.add( child ); 114 } 115 } 116 117 message = "The build could not read " + children.size() + " project" + ( children.size() == 1 ? "" : "s" ); 118 } 119 else 120 { 121 message = getMessage( message, exception ); 122 } 123 124 return new ExceptionSummary( exception, message, reference, children ); 125 } 126 127 private ExceptionSummary handle( ProjectBuildingResult result ) 128 { 129 List<ExceptionSummary> children = new ArrayList<>(); 130 131 for ( ModelProblem problem : result.getProblems() ) 132 { 133 ExceptionSummary child = handle( problem, result.getProjectId() ); 134 if ( child != null ) 135 { 136 children.add( child ); 137 } 138 } 139 140 if ( children.isEmpty() ) 141 { 142 return null; 143 } 144 145 String message = 146 "\nThe project " + result.getProjectId() + " (" + result.getPomFile() + ") has " 147 + children.size() + " error" + ( children.size() == 1 ? "" : "s" ); 148 149 return new ExceptionSummary( null, message, null, children ); 150 } 151 152 private ExceptionSummary handle( ModelProblem problem, String projectId ) 153 { 154 if ( ModelProblem.Severity.ERROR.compareTo( problem.getSeverity() ) >= 0 ) 155 { 156 String message = problem.getMessage(); 157 158 String location = ModelProblemUtils.formatLocation( problem, projectId ); 159 160 if ( StringUtils.isNotEmpty( location ) ) 161 { 162 message += " @ " + location; 163 } 164 165 return handle( message, problem.getException() ); 166 } 167 else 168 { 169 return null; 170 } 171 } 172 173 private String getReference( Throwable exception ) 174 { 175 String reference = ""; 176 177 if ( exception != null ) 178 { 179 if ( exception instanceof MojoExecutionException ) 180 { 181 reference = MojoExecutionException.class.getSimpleName(); 182 183 Throwable cause = exception.getCause(); 184 if ( cause instanceof IOException ) 185 { 186 cause = cause.getCause(); 187 if ( cause instanceof ConnectException ) 188 { 189 reference = ConnectException.class.getSimpleName(); 190 } 191 } 192 } 193 else if ( exception instanceof MojoFailureException ) 194 { 195 reference = MojoFailureException.class.getSimpleName(); 196 } 197 else if ( exception instanceof LinkageError ) 198 { 199 reference = LinkageError.class.getSimpleName(); 200 } 201 else if ( exception instanceof PluginExecutionException ) 202 { 203 Throwable cause = exception.getCause(); 204 205 if ( cause instanceof PluginContainerException ) 206 { 207 Throwable cause2 = cause.getCause(); 208 209 if ( cause2 instanceof NoClassDefFoundError 210 && cause2.getMessage().contains( "org/sonatype/aether/" ) ) 211 { 212 reference = "AetherClassNotFound"; 213 } 214 } 215 216 if ( StringUtils.isEmpty( reference ) ) 217 { 218 reference = getReference( cause ); 219 } 220 221 if ( StringUtils.isEmpty( reference ) ) 222 { 223 reference = exception.getClass().getSimpleName(); 224 } 225 } 226 else if ( exception instanceof LifecycleExecutionException ) 227 { 228 reference = getReference( exception.getCause() ); 229 } 230 else if ( isNoteworthyException( exception ) ) 231 { 232 reference = exception.getClass().getSimpleName(); 233 } 234 } 235 236 if ( StringUtils.isNotEmpty( reference ) && !reference.startsWith( "http:" ) ) 237 { 238 reference = "http://cwiki.apache.org/confluence/display/MAVEN/" + reference; 239 } 240 241 return reference; 242 } 243 244 private boolean isNoteworthyException( Throwable exception ) 245 { 246 if ( exception == null ) 247 { 248 return false; 249 } 250 else if ( exception instanceof Error ) 251 { 252 return true; 253 } 254 else if ( exception instanceof RuntimeException ) 255 { 256 return false; 257 } 258 else if ( exception.getClass().getName().startsWith( "java" ) ) 259 { 260 return false; 261 } 262 return true; 263 } 264 265 private String getMessage( String message, Throwable exception ) 266 { 267 String fullMessage = ( message != null ) ? message : ""; 268 269 for ( Throwable t = exception; t != null; t = t.getCause() ) 270 { 271 String exceptionMessage = t.getMessage(); 272 273 if ( t instanceof AbstractMojoExecutionException ) 274 { 275 String longMessage = ( (AbstractMojoExecutionException) t ).getLongMessage(); 276 if ( StringUtils.isNotEmpty( longMessage ) ) 277 { 278 if ( StringUtils.isEmpty( exceptionMessage ) || longMessage.contains( exceptionMessage ) ) 279 { 280 exceptionMessage = longMessage; 281 } 282 else if ( !exceptionMessage.contains( longMessage ) ) 283 { 284 exceptionMessage = join( exceptionMessage, '\n' + longMessage ); 285 } 286 } 287 } 288 289 if ( StringUtils.isEmpty( exceptionMessage ) ) 290 { 291 exceptionMessage = t.getClass().getSimpleName(); 292 } 293 294 if ( t instanceof UnknownHostException && !fullMessage.contains( "host" ) ) 295 { 296 fullMessage = join( fullMessage, "Unknown host " + exceptionMessage ); 297 } 298 else if ( !fullMessage.contains( exceptionMessage ) ) 299 { 300 fullMessage = join( fullMessage, exceptionMessage ); 301 } 302 } 303 304 return fullMessage.trim(); 305 } 306 307 private String join( String message1, String message2 ) 308 { 309 String message = ""; 310 311 if ( StringUtils.isNotEmpty( message1 ) ) 312 { 313 message = message1.trim(); 314 } 315 316 if ( StringUtils.isNotEmpty( message2 ) ) 317 { 318 if ( StringUtils.isNotEmpty( message ) ) 319 { 320 if ( message.endsWith( "." ) || message.endsWith( "!" ) || message.endsWith( ":" ) ) 321 { 322 message += " "; 323 } 324 else 325 { 326 message += ": "; 327 } 328 } 329 330 message += message2; 331 } 332 333 return message; 334 } 335 336}