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    }