001/*
002 * JGrapes Event Driven Framework
003 * Copyright (C) 2016-2018 Michael N. Lipp
004 * 
005 * This program is free software; you can redistribute it and/or modify it 
006 * under the terms of the GNU Affero General Public License as published by 
007 * the Free Software Foundation; either version 3 of the License, or 
008 * (at your option) any later version.
009 * 
010 * This program is distributed in the hope that it will be useful, but 
011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 
013 * for more details.
014 * 
015 * You should have received a copy of the GNU Affero General Public License along 
016 * with this program; if not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jgrapes.core.events;
020
021import java.lang.reflect.InvocationTargetException;
022
023import org.jgrapes.core.Channel;
024import org.jgrapes.core.Components;
025import org.jgrapes.core.Event;
026
027/**
028 * This event signals that an error occurred while processing an event.
029 */
030public class Error extends Event<Void> {
031
032    private final Event<?> event;
033    private final String message;
034    private Throwable throwable;
035
036    /**
037     * Creates a new event as a copy of an existing event. Useful
038     * for forwarding an event. Constructors "`<T extend Error> T(T event)`"
039     * must be implemented by all derived classes.
040     *
041     * @param event the event to copy
042     */
043    public Error(Error event) {
044        this.event = event.event;
045        this.message = event.message;
046        this.throwable = event.throwable;
047    }
048
049    /**
050     * Duplicate the event. Returns a new event with the same class
051     * and the same properties of the given event. Relies on the
052     * proper implementation of constructors "`<T extend Error> T(T event)`"
053     * for derived classes.
054     * 
055     * Creating a duplicate id useful for forwarding a derived `Error` while
056     * handling a base class.
057     *
058     * @param <T> the generic type
059     * @param event the event
060     * @return the t
061     */
062    @SuppressWarnings({ "unchecked",
063        "PMD.AvoidThrowingNewInstanceOfSameException" })
064    public static <T extends Error> T duplicate(T event) {
065        try {
066            return (T) event.getClass().getConstructor(event.getClass())
067                .newInstance(event);
068        } catch (InstantiationException | IllegalAccessException
069                | IllegalArgumentException | InvocationTargetException
070                | NoSuchMethodException | SecurityException e) {
071            throw new IllegalArgumentException(e);
072        }
073    }
074
075    /**
076     * Creates a new event.
077     * 
078     * @param event the event being processed when the problem occurred
079     * @param message the message
080     */
081    public Error(Event<?> event, String message) {
082        this.event = event;
083        this.message = message;
084    }
085
086    /**
087     * Creates a new event caused by the given throwable.
088     * 
089     * @param event the event being processed when the problem occurred
090     * @param message the message
091     * @param throwable the throwable
092     */
093    public Error(Event<?> event, String message, Throwable throwable) {
094        this.event = event;
095        this.message = message;
096        this.throwable = throwable;
097    }
098
099    /**
100     * Creates a new event caused by the given throwable. The message
101     * is initialized from the throwable.
102     * 
103     * @param event the event being processed when the problem occurred
104     * @param throwable the throwable
105     */
106    public Error(Event<?> event, Throwable throwable) {
107        this.event = event;
108        this.message = throwable.getMessage() == null
109            ? throwable.getClass().getName()
110            : throwable.getMessage();
111        this.throwable = throwable;
112    }
113
114    /**
115     * Returns the event that was handled when the problem occurred.
116     * 
117     * @return the event
118     */
119    public Event<?> event() {
120        return event;
121    }
122
123    /**
124     * Returns the message passed to the constructor.
125     * 
126     * @return the message
127     */
128    public String message() {
129        return message;
130    }
131
132    /**
133     * Returns the throwable that caused the problem.
134     * 
135     * @return the throwable or {@code null} if the problem wasn't caused
136     * by a throwable.
137     */
138    public Throwable throwable() {
139        return throwable;
140    }
141
142    /*
143     * (non-Javadoc)
144     * 
145     * @see java.lang.Object#toString()
146     */
147    @Override
148    public String toString() {
149        StringBuilder builder = new StringBuilder();
150        builder.append(Components.objectName(this))
151            .append(" [");
152        if (channels().length > 0) {
153            builder.append("channels=");
154            builder.append(Channel.toString(channels()));
155        }
156        if (message != null) {
157            builder.append(", message=\"");
158            builder.append(message);
159            builder.append('"');
160        }
161        if (event != null) {
162            builder.append(", caused by: ");
163            builder.append(event.toString());
164        }
165        builder.append(']');
166        return builder.toString();
167    }
168}