001 package 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
022 import java.io.IOException;
023 import java.net.ConnectException;
024 import java.net.UnknownHostException;
025 import java.util.ArrayList;
026 import java.util.List;
027
028 import org.apache.maven.lifecycle.LifecycleExecutionException;
029 import org.apache.maven.model.building.ModelProblem;
030 import org.apache.maven.model.building.ModelProblemUtils;
031 import org.apache.maven.plugin.AbstractMojoExecutionException;
032 import org.apache.maven.plugin.MojoExecutionException;
033 import org.apache.maven.plugin.MojoFailureException;
034 import org.apache.maven.plugin.PluginContainerException;
035 import org.apache.maven.plugin.PluginExecutionException;
036 import org.apache.maven.project.ProjectBuildingException;
037 import org.apache.maven.project.ProjectBuildingResult;
038 import org.codehaus.plexus.component.annotations.Component;
039 import 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
048 All 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
068 Plugins:
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 )
086 public 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 }