/**
* Copyright (C) 2012 FuseSource, Inc.
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.hawtdispatch.internal;
import org.fusesource.hawtdispatch.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import static java.lang.String.format;
/**
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
*/
final public class HawtCustomDispatchSource<Event, MergedEvent> extends AbstractDispatchObject implements CustomDispatchSource<Event, MergedEvent> {
public static final boolean DEBUG = false;
final AtomicBoolean canceled = new AtomicBoolean();
private Task cancelHandler;
private Task eventHandler;
private final ThreadLocal<MergedEvent> outboundEvent = new ThreadLocal<MergedEvent>();
private final ThreadLocal<MergedEvent> firedEvent = new ThreadLocal<MergedEvent>();
private final EventAggregator<Event, MergedEvent> aggregator;
private MergedEvent pendingEvent;
private final boolean ordered;
public HawtCustomDispatchSource(HawtDispatcher dispatcher, EventAggregator<Event, MergedEvent> aggregator, DispatchQueue queue) {
this.aggregator = aggregator;
this.suspended.incrementAndGet();
this.ordered = aggregator instanceof OrderedEventAggregator;
setTargetQueue(queue);
}
public MergedEvent getData() {
final MergedEvent rc = firedEvent.get();
firedEvent.set(null);
return rc;
}
protected final ConcurrentLinkedQueue<MergedEvent> externalQueue = new ConcurrentLinkedQueue<MergedEvent>();
protected final AtomicLong size = new AtomicLong();
public void merge(Event event) {
debug("merge called");
WorkerThread thread = WorkerThread.currentWorkerThread();
if( thread!=null ) {
MergedEvent previous = outboundEvent.get();
MergedEvent next = aggregator.mergeEvent(previous, event);
if( next==null ) {
debug("merge resulted in cancel");
outboundEvent.remove();
} else {
outboundEvent.set(next);
if( previous==null ) {
debug("first merge, posting deferred fire event");
if( ordered ) {
HawtDispatchQueue current = HawtDispatcher.CURRENT_QUEUE.get();
current.getSourceQueue().add(this);
} else {
thread.getDispatchQueue().getSourceQueue().add(this);
}
} else {
debug("there was a previous merge, no need to post deferred fire event");
}
}
} else {
debug("merge not called from a worker thread.. triggering fire event now");
fireEvent(aggregator.mergeEvent(null, event));
}
}
public void run() {
debug("deferred fire event executing");
fireEvent(outboundEvent.get());
outboundEvent.remove();
}
private void fireEvent(final MergedEvent event) {
if( event!=null ) {
targetQueue.execute(new Task() {
public void run() {
if( isCanceled() ) {
debug("canceled");
return;
}
if( isSuspended() ) {
debug("fired.. but suspended");
synchronized(HawtCustomDispatchSource.this) {
if( pendingEvent==null ) {
pendingEvent = event;
} else {
pendingEvent = aggregator.mergeEvents(pendingEvent, event);
}
}
} else {
MergedEvent e=null;
synchronized(HawtCustomDispatchSource.this) {
e = pendingEvent;
pendingEvent = null;
}
if( e!=null ) {
debug("fired.. mergined with previous pending event..");
e = aggregator.mergeEvents(e, event);
} else {
debug("fired.. no previous pending event..");
e = event;
}
firedEvent.set(e);
try {
eventHandler.run();
} catch (Throwable e1) {
Thread thread = Thread.currentThread();
thread.getUncaughtExceptionHandler().uncaughtException(thread, e1);
}
firedEvent.remove();
debug("eventHandler done");
}
}
});
}
}
@Override
protected void onStartup() {
if( eventHandler==null ) {
throw new IllegalArgumentException("eventHandler must be set");
}
onResume();
}
public void cancel() {
if( canceled.compareAndSet(false, true) ) {
targetQueue.execute(new Task() {
public void run() {
if( cancelHandler!=null ) {
cancelHandler.run();
}
}
});
}
}
@Override
protected void onResume() {
debug("onResume");
targetQueue.execute(new Task() {
public void run() {
if( isCanceled() ) {
return;
}
if( !isSuspended() ) {
MergedEvent e=null;
synchronized(HawtCustomDispatchSource.this) {
e = pendingEvent;
pendingEvent = null;
}
if( e!=null ) {
firedEvent.set(e);
eventHandler.run();
firedEvent.remove();
}
}
}
});
}
public boolean isCanceled() {
return canceled.get();
}
@Deprecated
public void setCancelHandler(Runnable handler) {
this.setCancelHandler(new TaskWrapper(handler));
}
@Deprecated
public void setEventHandler(Runnable handler) {
this.setEventHandler(new TaskWrapper(handler));
}
public void setCancelHandler(Task cancelHandler) {
this.cancelHandler = cancelHandler;
}
public void setEventHandler(Task eventHandler) {
this.eventHandler = eventHandler;
}
protected void debug(String str, Object... args) {
if (DEBUG) {
System.out.println(format("[DEBUG] HawtCustomDispatchSource %0#10x: ", System.identityHashCode(this))+format(str, args));
}
}
protected void debug(Throwable thrown, String str, Object... args) {
if (DEBUG) {
if (str != null) {
debug(str, args);
}
if (thrown != null) {
thrown.printStackTrace();
}
}
}
}