/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2016, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.gce.imagemosaic; import java.io.File; import java.util.EventListener; import java.util.EventObject; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.SwingUtilities; /** * Base class to handle events * * @author carlo cancellieri - GeoSolutions SAS * */ public class ImageMosaicEventHandlers { /** Default Logger * */ final static Logger LOGGER = org.geotools.util.logging.Logging .getLogger(ImageMosaicEventHandlers.class); /** * List containing all the objects that want to be notified during processing. */ protected List<ProcessingEventListener> notificationListeners = new CopyOnWriteArrayList<ProcessingEventListener>(); /** * Set this to false for command line UIs where the delayed event sending may prevent some messages to be seen before the tool exits, to true for * real GUI where you don't want the processing to be blocked too long, or when you have slow listeners in general. */ protected boolean sendDelayedMessages = false; static abstract public class ProcessingEventListener implements EventListener { abstract public void getNotification(final ProcessingEvent event); abstract public void exceptionOccurred(final ExceptionEvent event); } /** * @author Simone Giannecchini, GeoSolutions. * */ static public class ProcessingEvent extends EventObject { private static final long serialVersionUID = 6930580659705360225L; private String message = null; private double percentage = 0; /** * @param source */ public ProcessingEvent(final Object source, final String message, final double percentage) { super(source); this.message = message; this.percentage = percentage; } public double getPercentage() { return percentage; } public String getMessage() { return message; } } /** * A special ProcessingEvent raised when a file has completed/failed ingestion */ static public class FileProcessingEvent extends ProcessingEvent { private File file; private boolean ingested; /** * @param source */ public FileProcessingEvent(final Object source, final File file, final boolean ingested, final String message, final double percentage) { super(source, message, percentage); this.file = file; this.ingested = ingested; } public File getFile() { return file; } public boolean isIngested() { return ingested; } } /** * Event launched when an exception occurs. Percentage and message may be missing, in this case they will be -1 and the exception message * (localized if available, standard otherwise) * * @author aaime, TOPP. * */ static public final class ExceptionEvent extends ProcessingEvent { private static final long serialVersionUID = 2272452028229922551L; private Exception exception; public ExceptionEvent(Object source, String message, double percentage, Exception exception) { super(source, message, percentage); this.exception = exception; } public ExceptionEvent(Object source, Exception exception) { super(source, Utils.getMessageFromException(exception), -1); this.exception = exception; } public Exception getException() { return exception; } } /** * Private Class which simply fires the events using a copy of the listeners list in order to avoid problems with listeners that remove themselves * or are removed by someone else */ protected final static class ProgressEventDispatchThreadEventLauncher implements Runnable { /** * The event we want to fire away. */ private ProcessingEvent event; /** * The list of listeners. */ private Object[] listeners; /** * Default constructor. */ ProgressEventDispatchThreadEventLauncher() { } /** * Used to send an event to an array of listeners. * * @param evt is the {@link ProcessingEvent} to send. * @param listeners is the array of {@link ProcessingEventListener}s to notify. */ synchronized void setEvent(final ProcessingEvent evt, final Object[] listeners) { if (listeners == null || evt == null) throw new NullPointerException("Input argumentBuilder cannot be null"); this.listeners = listeners; this.event = evt; } /** * Run the event launcher */ public void run() { final int numListeners = listeners.length; if (event instanceof ExceptionEvent) for (int i = 0; i < numListeners; i++) ((ProcessingEventListener) listeners[i]) .exceptionOccurred((ExceptionEvent) this.event); else for (int i = 0; i < numListeners; i++) ((ProcessingEventListener) listeners[i]).getNotification(this.event); } } public ImageMosaicEventHandlers() { super(); } /** * Adding a listener to the {@link ProcessingEventListener}s' list. * * @param listener to add to the list of listeners. */ public final void addProcessingEventListener(final ProcessingEventListener listener) { notificationListeners.add(listener); } /** * Firing an event to listeners in order to inform them about what we are doing and about the percentage of work already carried out. * * @param level * * @param message The message to show. * @param percentage The percentage for the process. */ protected void fireEvent(Level level, final String inMessage, final double percentage) { if (LOGGER.isLoggable(level)) { LOGGER.log(level, inMessage); } synchronized (notificationListeners) { final String newLine = System.getProperty("line.separator"); final StringBuilder message = new StringBuilder("Thread Name "); message.append(Thread.currentThread().getName()).append(newLine); message.append(this.getClass().toString()).append(newLine).append(inMessage); final ProcessingEvent evt = new ProcessingEvent(this, message.toString(), percentage); ProgressEventDispatchThreadEventLauncher eventLauncher = new ProgressEventDispatchThreadEventLauncher(); eventLauncher.setEvent(evt, this.notificationListeners.toArray()); sendEvent(eventLauncher); } } /** * Firing an event to listeners in order to inform them about what we are doing and about the percentage of work already carried out. * * @param level * * @param message The message to show. * @param percentage The percentage for the process. */ protected void fireFileEvent(Level level, final File file, final boolean ingested, final String inMessage, final double percentage) { if (LOGGER.isLoggable(level)) { LOGGER.log(level, inMessage); } synchronized (notificationListeners) { final String newLine = System.getProperty("line.separator"); final StringBuilder message = new StringBuilder("Thread Name "); message.append(Thread.currentThread().getName()).append(newLine); message.append(this.getClass().toString()).append(newLine).append(inMessage); final FileProcessingEvent evt = new FileProcessingEvent(this, file, ingested, message.toString(), percentage); ProgressEventDispatchThreadEventLauncher eventLauncher = new ProgressEventDispatchThreadEventLauncher(); eventLauncher.setEvent(evt, this.notificationListeners.toArray()); sendEvent(eventLauncher); } } /** * Firing an exception event to listeners in order to inform them that processing broke and we can no longer proceed. This is a convenience * method, it will call {@link #fireException(String, double, Exception)} with the exception message and -1 as percentage. * * @param ex the actual exception occurred */ protected void fireException(Exception ex) { synchronized (notificationListeners) { fireException(Utils.getMessageFromException(ex), -1, ex); } } /** * Firing an exception event to listeners in order to inform them that processing broke and we can no longer proceed * * @param string The message to show. * @param percentage The percentage for the process. * @param ex the actual exception occurred */ private void fireException(final String string, final double percentage, Exception ex) { synchronized (notificationListeners) { final String newLine = System.getProperty("line.separator"); final StringBuilder message = new StringBuilder("Thread Name "); message.append(Thread.currentThread().getName()).append(newLine); message.append(this.getClass().toString()).append(newLine).append(string); final ExceptionEvent evt = new ExceptionEvent(this, string, percentage, ex); ProgressEventDispatchThreadEventLauncher eventLauncher = new ProgressEventDispatchThreadEventLauncher(); eventLauncher.setEvent(evt, this.notificationListeners.toArray()); sendEvent(eventLauncher); } } public boolean isSendDelayedMessages() { return sendDelayedMessages; } public void setSendDelayedMessages(boolean sendDelayedMessages) { this.sendDelayedMessages = sendDelayedMessages; } /** * Removing all the listeners. * */ public void removeAllProcessingEventListeners() { synchronized (notificationListeners) { notificationListeners.clear(); } } /** * Removing a {@link ProcessingEventListener} from the listeners' list. * * @param listener {@link ProcessingEventListener} to remove from the list of listeners. */ public void removeProcessingEventListener(final ProcessingEventListener listener) { notificationListeners.remove(listener); } private void sendEvent(ProgressEventDispatchThreadEventLauncher eventLauncher) { if (sendDelayedMessages) SwingUtilities.invokeLater(eventLauncher); else eventLauncher.run(); } }