/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.utility;
import java.io.PrintStream;
import java.io.Serializable;
/**
* Straightforward implementation of the ExceptionBroadcaster interface.
*/
public class SimpleExceptionBroadcaster
implements ExceptionBroadcaster, Serializable
{
/** The exception listeners. */
private ExceptionListener[] listeners;
/**
* The stream where exceptions are dumped when there are no
* listeners or when a listener triggers an exception while
* responding to the original exception
*/
private final PrintStream defaultStream;
private static final ExceptionListener[] EMPTY_LISTENERS = new ExceptionListener[0];
private static final long serialVersionUID = 1L;
// ********** constructor **********
/**
* Dump exceptions to the specified stream if there are no listeners.
*/
public SimpleExceptionBroadcaster(PrintStream defaultStream) {
super();
this.defaultStream = defaultStream;
}
/**
* Default constructor. Dump exceptions to the standard error stream
* if there are no listeners.
*/
public SimpleExceptionBroadcaster() {
this(System.err);
}
// ********** ExceptionBroadcaster implementation **********
/**
* Broadcast the specified exception to the broadcaster's listeners.
*/
public void broadcast(Thread thread, Throwable exception) {
ExceptionListener[] currentListeners = null;
synchronized (this) {
if (this.listeners != null) {
int len = this.listeners.length;
currentListeners = new ExceptionListener[len];
System.arraycopy(this.listeners, 0, currentListeners, 0, len);
}
}
if (currentListeners == null) {
this.broadcastOnDefaultStream(thread, exception);
} else {
for (int i = currentListeners.length; i-- > 0; ) {
try {
currentListeners[i].exceptionThrown(thread, exception);
} catch (Throwable ex2) {
// if we have a problem during the notification, dump to the default stream
this.broadcastOnDefaultStream(Thread.currentThread(), ex2);
this.broadcastOnDefaultStream(thread, exception);
}
}
}
}
/**
* @see ExceptionBroadcaster#addExceptionListener(ExceptionListener)
*/
public synchronized void addExceptionListener(ExceptionListener listener) {
if (listener == null) {
throw new NullPointerException(); // better sooner than later
}
int len = 0;
ExceptionListener[] newListeners;
if (this.listeners == null) {
newListeners = new ExceptionListener[1];
} else {
len = this.listeners.length;
newListeners = new ExceptionListener[len + 1];
System.arraycopy(this.listeners, 0, newListeners, 0, len);
}
newListeners[len] = listener;
this.listeners = newListeners;
}
/**
* @see ExceptionBroadcaster#removeExceptionListener(ExceptionListener)
*/
public synchronized void removeExceptionListener(ExceptionListener listener) {
if (this.listeners == null) {
throw new IllegalArgumentException("listener not registered");
}
int len = this.listeners.length;
int index = len;
while (index-- > 0) {
if (this.listeners[index] == listener) { // use identity
break;
}
}
if (index == -1) {
throw new IllegalArgumentException("listener not registered");
}
if (len == 1) {
this.listeners = null;
} else {
ExceptionListener[] newListeners = new ExceptionListener[len - 1];
if (index != 0) {
System.arraycopy(this.listeners, 0, newListeners, 0, index);
}
int next = index + 1;
if (next != len) {
System.arraycopy(this.listeners, next, newListeners, index, len - next);
}
this.listeners = newListeners;
}
}
// ********** public API **********
/**
* Return the broadcaster's exception listeners.
*/
public synchronized ExceptionListener[] getExceptionListeners() {
if (this.listeners == null) {
return EMPTY_LISTENERS;
}
int len = this.listeners.length;
ExceptionListener[] result = new ExceptionListener[len];
System.arraycopy(this.listeners, 0, result, 0, len);
return result;
}
/**
* Return whether the broadcaster has any exception listeners.
*/
public synchronized boolean hasExceptionListeners() {
return this.listeners != null;
}
/**
* Return whether the broadcaster has no exception listeners.
*/
public synchronized boolean hasNoExceptionListeners() {
return this.listeners == null;
}
/**
* @see Object#toString()
*/
public String toString() {
return ClassTools.shortClassNameForObject(this);
}
// ********** protected API **********
/**
* Broadcast the exception to the "default" stream.
* "If a tree falls in the forest...?"
*/
protected void broadcastOnDefaultStream(Thread thread, Throwable exception) {
this.broadcastOn(thread, exception, this.defaultStream);
}
/**
* Broadcast the exception to the specified stream.
*/
protected void broadcastOn(Thread thread, Throwable exception, PrintStream stream) {
synchronized (stream) {
stream.println(thread);
exception.printStackTrace(stream);
}
}
}