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;
022import org.jgrapes.core.Channel;
023import org.jgrapes.core.Components;
024import org.jgrapes.core.Event;
025
026/**
027 * This event signals that an error occurred while processing an event.
028 */
029public class Error extends Event<Void> {
030
031    private final Event<?> event;
032    private final String message;
033    private Throwable throwable;
034
035    /**
036     * Creates a new event as a copy of an existing event. Useful
037     * for forwarding an event. Constructors "`<T extend Error> T(T event)`"
038     * must be implemented by all derived classes.
039     *
040     * @param event the event to copy
041     */
042    public Error(Error event) {
043        this.event = event.event;
044        this.message = event.message;
045        this.throwable = event.throwable;
046    }
047
048    /**
049     * Duplicate the event. Returns a new event with the same class
050     * and the same properties of the given event. Relies on the
051     * proper implementation of constructors "`<T extend Error> T(T event)`"
052     * for derived classes.
053     * 
054     * Creating a duplicate id useful for forwarding a derived `Error` while
055     * handling a base class.
056     *
057     * @param <T> the generic type
058     * @param event the event
059     * @return the t
060     */
061    @SuppressWarnings({ "unchecked",
062        "PMD.AvoidThrowingNewInstanceOfSameException" })
063    public static <T extends Error> T duplicate(T event) {
064        try {
065            return (T) event.getClass().getConstructor(event.getClass())
066                .newInstance(event);
067        } catch (InstantiationException | IllegalAccessException
068                | IllegalArgumentException | InvocationTargetException
069                | NoSuchMethodException | SecurityException e) {
070            throw new IllegalArgumentException(e);
071        }
072    }
073
074    /**
075     * Creates a new event.
076     * 
077     * @param event the event being processed when the problem occurred
078     * @param message the message
079     */
080    public Error(Event<?> event, String message) {
081        this.event = event;
082        this.message = message;
083    }
084
085    /**
086     * Creates a new event caused by the given throwable.
087     * 
088     * @param event the event being processed when the problem occurred
089     * @param message the message
090     * @param throwable the throwable
091     */
092    public Error(Event<?> event, String message, Throwable throwable) {
093        this.event = event;
094        this.message = message;
095        this.throwable = throwable;
096    }
097
098    /**
099     * Creates a new event caused by the given throwable. The message
100     * is initialized from the throwable.
101     * 
102     * @param event the event being processed when the problem occurred
103     * @param throwable the throwable
104     */
105    public Error(Event<?> event, Throwable throwable) {
106        this.event = event;
107        this.message = throwable.getMessage() == null
108            ? throwable.getClass().getName()
109            : throwable.getMessage();
110        this.throwable = throwable;
111    }
112
113    /**
114     * Returns the event that was handled when the problem occurred.
115     * 
116     * @return the event
117     */
118    public Event<?> event() {
119        return event;
120    }
121
122    /**
123     * Returns the message passed to the constructor.
124     * 
125     * @return the message
126     */
127    public String message() {
128        return message;
129    }
130
131    /**
132     * Returns the throwable that caused the problem.
133     * 
134     * @return the throwable or {@code null} if the problem wasn't caused
135     * by a throwable.
136     */
137    public Throwable throwable() {
138        return throwable;
139    }
140
141    /*
142     * (non-Javadoc)
143     * 
144     * @see java.lang.Object#toString()
145     */
146    @Override
147    public String toString() {
148        StringBuilder builder = new StringBuilder();
149        builder.append(Components.objectName(this))
150            .append(" [");
151        if (channels().length > 0) {
152            builder.append("channels=");
153            builder.append(Channel.toString(channels()));
154        }
155        if (message != null) {
156            builder.append(", message=\"");
157            builder.append(message);
158            builder.append('"');
159        }
160        if (event != null) {
161            builder.append(", caused by: ");
162            builder.append(event.toString());
163        }
164        builder.append(']');
165        return builder.toString();
166    }
167}