001// Licensed under the Apache License, Version 2.0 (the "License");
002// you may not use this file except in compliance with the License.
003// You may obtain a copy of the License at
004//
005// http://www.apache.org/licenses/LICENSE-2.0
006//
007// Unless required by applicable law or agreed to in writing, software
008// distributed under the License is distributed on an "AS IS" BASIS,
009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
010// See the License for the specific language governing permissions and
011// limitations under the License.
012
013package org.apache.tapestry5.internal.services.exceptions;
014
015import org.apache.tapestry5.SymbolConstants;
016import org.apache.tapestry5.commons.util.ExceptionUtils;
017import org.apache.tapestry5.ioc.annotations.Inject;
018import org.apache.tapestry5.ioc.annotations.Symbol;
019import org.apache.tapestry5.ioc.internal.util.InternalUtils;
020import org.apache.tapestry5.services.ExceptionReportWriter;
021import org.apache.tapestry5.services.ExceptionReporter;
022import org.slf4j.Logger;
023
024import java.io.File;
025import java.io.IOException;
026import java.io.PrintWriter;
027import java.util.Date;
028import java.util.concurrent.atomic.AtomicInteger;
029
030@SuppressWarnings("ResultOfMethodCallIgnored")
031public class ExceptionReporterImpl implements ExceptionReporter
032{
033
034    @Inject
035    @Symbol(SymbolConstants.EXCEPTION_REPORTS_DIR)
036    private File logDir;
037
038    @Inject
039    @Symbol(SymbolConstants.RESTRICTIVE_ENVIRONMENT)
040    private boolean restrictive;
041
042    private final AtomicInteger uid = new AtomicInteger();
043
044    @Inject
045    private Logger logger;
046
047
048    @Inject
049    private ExceptionReportWriter exceptionReportWriter;
050
051    @Override
052    public void reportException(Throwable exception)
053    {
054        Date date = new Date();
055        String fileName = String.format(
056                "exception-%tY%<tm%<td-%<tH%<tM%<tS-%<tL.%d.txt", date,
057                uid.getAndIncrement());
058
059        File folder = getOutputFolder(date);
060
061        try
062        {
063            if (! restrictive)
064            {
065                folder.mkdirs();
066            }
067            File log = new File(folder, fileName);
068            writeExceptionToFile(exception, log);
069
070            logger.warn(String.format("Wrote exception report to %s", toURI(log)));
071        } catch (Exception ex)
072        {
073            logger.error(String.format("Unable to write exception report %s at %s: %s",
074                    fileName, folder.getAbsolutePath(), ExceptionUtils.toMessage(ex)));
075
076            logger.error("Original exception:", exception);
077        }
078    }
079
080    /**
081     * Get the path of the directory in which the exception report file(s) should
082     * be written. Except in "restrictive" environments like GAE, this is a
083     * dated sub-directory of the one specified in the
084     * tapestry.exception-reports-dir symbol.
085     * 
086     * @param date the date to be used if a dated directory is needed
087     * @return the File object representing the folder
088     */
089    private File getOutputFolder(Date date)
090    {
091        if (restrictive)
092        {
093            // Good luck with this; all exceptions written to a single folder.
094            return logDir;
095        } else
096        {
097            String folderName = String.format("%tY-%<tm-%<td/%<tH/%<tM", date);
098            return new File(logDir, folderName);
099        }
100
101    }
102
103    private String toURI(File file)
104    {
105        try
106        {
107            return file.toURI().toString();
108        } catch (Exception e)
109        {
110            return file.toString();
111        }
112    }
113
114    private void writeExceptionToFile(Throwable exception, File log) throws IOException
115    {
116        log.createNewFile();
117
118        PrintWriter writer = null;
119
120        try
121        {
122            writer = new PrintWriter(log);
123            exceptionReportWriter.writeReport(writer, exception);
124        } finally
125        {
126            InternalUtils.close(writer);
127        }
128    }
129
130
131}