001/*
002 *  jDTAUS Core Utilities
003 *  Copyright (C) 2005 Christian Schulte
004 *  <cs@schulte.it>
005 *
006 *  This library is free software; you can redistribute it and/or
007 *  modify it under the terms of the GNU Lesser General Public
008 *  License as published by the Free Software Foundation; either
009 *  version 2.1 of the License, or any later version.
010 *
011 *  This library is distributed in the hope that it will be useful,
012 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 *  Lesser General Public License for more details.
015 *
016 *  You should have received a copy of the GNU Lesser General Public
017 *  License along with this library; if not, write to the Free Software
018 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
019 *
020 */
021package org.jdtaus.core.lang.util;
022
023import java.io.File;
024import java.net.URL;
025import org.jdtaus.core.container.ContainerFactory;
026import org.jdtaus.core.container.Implementation;
027import org.jdtaus.core.container.ModelFactory;
028import org.jdtaus.core.container.Specification;
029import org.jdtaus.core.lang.ExceptionEvent;
030import org.jdtaus.core.lang.ExceptionListener;
031import org.jdtaus.core.messages.BugReportMessage;
032import org.jdtaus.core.messages.ExceptionMessage;
033import org.jdtaus.core.messages.UndefinedApplicationStateMessage;
034import org.jdtaus.core.text.Message;
035import org.jdtaus.core.text.MessageEvent;
036import org.jdtaus.core.text.Messages;
037import org.jdtaus.core.text.spi.ApplicationLogger;
038
039/**
040 * {@code ExceptionListener} resolving exceptions to corresponding application
041 * messages.
042 *
043 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
044 * @version $JDTAUS: ExceptionMessageProducer.java 8641 2012-09-27 06:45:17Z schulte $
045 *
046 * @see #onException(ExceptionEvent)
047 * @see org.jdtaus.core.text.util.SwingMessagePane
048 * @see org.jdtaus.core.text.util.MessageLogger
049 */
050public final class ExceptionMessageProducer implements ExceptionListener
051{
052    //--Constructors------------------------------------------------------------
053
054// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausConstructors
055    // This section is managed by jdtaus-container-mojo.
056
057// </editor-fold>//GEN-END:jdtausConstructors
058
059    //------------------------------------------------------------Constructors--
060    //--Dependencies------------------------------------------------------------
061
062// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies
063    // This section is managed by jdtaus-container-mojo.
064
065    /**
066     * Gets the configured <code>ApplicationLogger</code> implementation.
067     *
068     * @return The configured <code>ApplicationLogger</code> implementation.
069     */
070    private ApplicationLogger getApplicationLogger()
071    {
072        return (ApplicationLogger) ContainerFactory.getContainer().
073            getDependency( this, "ApplicationLogger" );
074
075    }
076
077// </editor-fold>//GEN-END:jdtausDependencies
078
079    //------------------------------------------------------------Dependencies--
080    //--Properties--------------------------------------------------------------
081
082// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties
083    // This section is managed by jdtaus-container-mojo.
084
085// </editor-fold>//GEN-END:jdtausProperties
086
087    //--------------------------------------------------------------Properties--
088    //--ExceptionListener-------------------------------------------------------
089
090    /**
091     * {@inheritDoc}
092     * <p>This method resolves exceptions provided by an application's
093     * {@code ExceptionEventSource} to corresponding {@code MessageEvent}s which
094     * an application may react to by registering a listener to its
095     * {@code MessageEventSource}. Messages for checked exceptions are resolved
096     * by using any available {@code ExceptionMessageResolver} implementation
097     * stopping at the first implementation not returning {@code null}.
098     * For exceptions which are instances of {@code RuntimeException} an
099     * additional message informing the user that the application's state is
100     * undefined and that a restart is recommended is added to the produced
101     * {@code MessageEvent}. Optionally an additional message informing the user
102     * on how to report a bug is added to the produced {@code MessageEvent} if
103     * the instance got constructed using the constructor taking that
104     * information.</p>
105     *
106     * @param event the event holding the exception.
107     *
108     * @see ExceptionMessageResolver#resolve(Exception)
109     */
110    public void onException( final ExceptionEvent event )
111    {
112        if ( event != null )
113        {
114            final Throwable exception = event.getException();
115            final Throwable rootCause = event.getRootCause();
116            final Messages messages = new Messages();
117
118            messages.addMessage( new ExceptionMessage( exception ) );
119
120            if ( rootCause instanceof RuntimeException )
121            {
122                messages.addMessage( new UndefinedApplicationStateMessage() );
123
124                if ( this.logDirectory != null )
125                {
126                    messages.addMessage(
127                        new BugReportMessage( this.logDirectory,
128                                              this.trackerUrl,
129                                              this.reportAddress ) );
130
131                }
132            }
133            else if ( rootCause instanceof Exception )
134            {
135                final Message[] resolved =
136                    this.resolveMessages( (Exception) rootCause );
137
138                if ( resolved != null )
139                {
140                    messages.addMessages( resolved );
141                }
142            }
143
144            this.getApplicationLogger().log(
145                new MessageEvent( this,
146                                  messages.getMessages(),
147                                  MessageEvent.ERROR ) );
148
149        }
150    }
151
152    //-------------------------------------------------------ExceptionListener--
153    //--ExceptionReporter-------------------------------------------------------
154
155    /**
156     * Directory holding the application's log files.
157     * @serial
158     */
159    private File logDirectory;
160
161    /**
162     * URL of the online bugtracking system.
163     * @serial
164     */
165    private URL trackerUrl;
166
167    /**
168     * Mail address to send the bugreport to.
169     * @serial
170     */
171    private String reportAddress;
172
173    /** Creates a new {@code ExceptionMessageProducer} instance. */
174    public ExceptionMessageProducer()
175    {
176        super();
177        this.logDirectory = null;
178        this.trackerUrl = null;
179        this.reportAddress = null;
180    }
181
182    /**
183     * Creates a new {@code ExceptionMessageProducer} instance taking the
184     * application's logfile directory, an URL to the application's online
185     * bugtracking system, and an email address where to send bugreports to
186     * alternatively.
187     *
188     * @param logDirectory the directory holding the application's logfiles.
189     * @param trackerUrl an URL to the application's online bugtracking system.
190     * @param reportAddress an email address to alternatively send bugreports
191     * to.
192     *
193     * @throws NullPointerException if either {@code logDirectory},
194     * {@code trackerUrl} or {@code reportAddress} is {@code null}.
195     */
196    public ExceptionMessageProducer( final File logDirectory,
197                                     final URL trackerUrl,
198                                     final String reportAddress )
199    {
200        super();
201        if ( logDirectory == null )
202        {
203            throw new NullPointerException( "logDirectory" );
204        }
205        if ( trackerUrl == null )
206        {
207            throw new NullPointerException( "trackerUrl" );
208        }
209        if ( reportAddress == null )
210        {
211            throw new NullPointerException( "reportAddress" );
212        }
213
214        this.logDirectory = logDirectory;
215        this.trackerUrl = trackerUrl;
216        this.reportAddress = reportAddress;
217    }
218
219    /**
220     * Resolves application messages for a given exception by querying any
221     * available {@code ExceptionMessageResolver} implementation stopping at the
222     * first implementation not returning {@code null}.
223     *
224     * @param exception the exception to resolve application messages for.
225     *
226     * @throws NullPointerException if {@code exception} is {@code null}.
227     */
228    private Message[] resolveMessages( final Exception exception )
229    {
230        if ( exception == null )
231        {
232            throw new NullPointerException( "exception" );
233        }
234
235        Message[] messages = null;
236        final Specification spec = ModelFactory.getModel().getModules().
237            getSpecification( ExceptionMessageResolver.class.getName() );
238
239        final Implementation[] resolvers = spec.getImplementations().
240            getImplementations();
241
242        for ( int i = resolvers.length - 1; i >= 0 && messages == null; i-- )
243        {
244            final ExceptionMessageResolver resolver =
245                (ExceptionMessageResolver) ContainerFactory.getContainer().
246                getObject( ExceptionMessageResolver.class,
247                           resolvers[i].getName() );
248
249            messages = resolver.resolve( exception );
250        }
251
252        return messages;
253    }
254
255    //-------------------------------------------------------ExceptionReporter--
256}