    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.ioc.annotations.Inject;
    017import org.apache.tapestry5.ioc.annotations.Symbol;
    018import org.apache.tapestry5.ioc.internal.util.InternalUtils;
    019import org.apache.tapestry5.ioc.util.ExceptionUtils;
    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}