mirror of
https://github.com/Rogiel/l2jserver2
synced 2025-12-06 07:32:46 +00:00
WorldEventFuture implementation
Signed-off-by: Rogiel <rogiel@rogiel.com>
This commit is contained in:
7
pom.xml
7
pom.xml
@@ -120,6 +120,13 @@
|
|||||||
<type>jar</type>
|
<type>jar</type>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>r09</version>
|
||||||
|
<type>jar</type>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<issueManagement>
|
<issueManagement>
|
||||||
|
|||||||
@@ -102,6 +102,11 @@ public class CachedWorldIDService extends AbstractService implements
|
|||||||
loaded = true;
|
loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unload() {
|
||||||
|
cache.removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the pre-existing ids
|
* Load the pre-existing ids
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ public interface WorldIDService extends Service {
|
|||||||
*/
|
*/
|
||||||
void load();
|
void load();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unload all loaded {@link ObjectID}
|
||||||
|
*/
|
||||||
|
void unload();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to resolve an ID based on its raw value
|
* Tries to resolve an ID based on its raw value
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ public class WorldServiceImpl extends AbstractService implements WorldService {
|
|||||||
protected void doStart() throws ServiceStartException {
|
protected void doStart() throws ServiceStartException {
|
||||||
objects.clear();
|
objects.clear();
|
||||||
idService.load();
|
idService.load();
|
||||||
|
dispatcher.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -172,5 +173,7 @@ public class WorldServiceImpl extends AbstractService implements WorldService {
|
|||||||
@Override
|
@Override
|
||||||
protected void doStop() throws ServiceStopException {
|
protected void doStop() throws ServiceStopException {
|
||||||
objects.clear();
|
objects.clear();
|
||||||
|
idService.unload();
|
||||||
|
dispatcher.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,10 +31,14 @@ public interface WorldEventDispatcher {
|
|||||||
* need to invoke listeners immediately. Dispatching <b>can</b> occur
|
* need to invoke listeners immediately. Dispatching <b>can</b> occur
|
||||||
* concurrently.
|
* concurrently.
|
||||||
*
|
*
|
||||||
|
* @param <E>
|
||||||
|
* the event type
|
||||||
* @param event
|
* @param event
|
||||||
* the event
|
* the event
|
||||||
|
* @return the future. The future can be used to be notified once the event
|
||||||
|
* has been dispatched to all listeners.
|
||||||
*/
|
*/
|
||||||
void dispatch(WorldEvent event);
|
<E extends WorldEvent> WorldEventFuture<E> dispatch(E event);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new global <tt>listener</tt>
|
* Adds a new global <tt>listener</tt>
|
||||||
|
|||||||
@@ -21,10 +21,14 @@ import java.util.Queue;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.AbstractFuture;
|
||||||
import com.l2jserver.model.id.ObjectID;
|
import com.l2jserver.model.id.ObjectID;
|
||||||
import com.l2jserver.model.world.WorldObject;
|
import com.l2jserver.model.world.WorldObject;
|
||||||
import com.l2jserver.util.factory.CollectionFactory;
|
import com.l2jserver.util.factory.CollectionFactory;
|
||||||
@@ -35,28 +39,51 @@ import com.l2jserver.util.factory.CollectionFactory;
|
|||||||
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
||||||
*/
|
*/
|
||||||
public class WorldEventDispatcherImpl implements WorldEventDispatcher {
|
public class WorldEventDispatcherImpl implements WorldEventDispatcher {
|
||||||
|
/**
|
||||||
|
* The logger
|
||||||
|
*/
|
||||||
private static final Logger log = LoggerFactory
|
private static final Logger log = LoggerFactory
|
||||||
.getLogger(WorldEventDispatcherImpl.class);
|
.getLogger(WorldEventDispatcherImpl.class);
|
||||||
|
|
||||||
private final Timer timer = new Timer();
|
/**
|
||||||
|
* The execution thread
|
||||||
|
*/
|
||||||
|
private Timer timer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of all global listeners
|
||||||
|
*/
|
||||||
private Set<WorldListener> globalListeners = CollectionFactory.newSet();
|
private Set<WorldListener> globalListeners = CollectionFactory.newSet();
|
||||||
|
/**
|
||||||
|
* The {@link Map} containing all listeners for every object
|
||||||
|
*/
|
||||||
private Map<ObjectID<?>, Set<WorldListener>> listeners = CollectionFactory
|
private Map<ObjectID<?>, Set<WorldListener>> listeners = CollectionFactory
|
||||||
.newMap();
|
.newMap();
|
||||||
// private Queue<ListenerIDPair> listeners = CollectionFactory
|
/**
|
||||||
// .newConcurrentQueue(ListenerIDPair.class);
|
* The events pending dispatch
|
||||||
private Queue<WorldEvent> events = CollectionFactory.newConcurrentQueue();
|
*/
|
||||||
|
private Queue<EventContainer> events = CollectionFactory
|
||||||
|
.newConcurrentQueue();
|
||||||
|
|
||||||
public WorldEventDispatcherImpl() {
|
public void start() {
|
||||||
|
timer = new Timer();
|
||||||
timer.scheduleAtFixedRate(new TimerTask() {
|
timer.scheduleAtFixedRate(new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final WorldEvent event = events.poll();
|
final EventContainer event = events.poll();
|
||||||
if (event == null)
|
if (event == null)
|
||||||
return;
|
return;
|
||||||
try {
|
try {
|
||||||
doDispatch(event);
|
// set state
|
||||||
|
event.future.running = true;
|
||||||
|
event.future.complete = false;
|
||||||
|
|
||||||
|
// dispatch
|
||||||
|
if (doDispatch(event))
|
||||||
|
// the set will update state
|
||||||
|
event.future.set(event.event);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
|
event.future.setException(t);
|
||||||
log.warn("Exception in WorldEventDispatcher thread", t);
|
log.warn("Exception in WorldEventDispatcher thread", t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,21 +91,32 @@ public class WorldEventDispatcherImpl implements WorldEventDispatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispatch(WorldEvent event) {
|
public <E extends WorldEvent> WorldEventFuture<E> dispatch(E event) {
|
||||||
log.debug("Queing dispatch for event {}", event);
|
log.debug("Queing dispatch for event {}", event);
|
||||||
events.add(event);
|
final WorldEventFutureImpl<E> future = new WorldEventFutureImpl<E>();
|
||||||
|
events.add(new EventContainer(event, future));
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void doDispatch(WorldEvent event) {
|
/**
|
||||||
|
* Do the dispatching
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* the event
|
||||||
|
* @return true if dispatch was not canceled
|
||||||
|
*/
|
||||||
|
public boolean doDispatch(EventContainer event) {
|
||||||
log.debug("Dispatching event {}", event);
|
log.debug("Dispatching event {}", event);
|
||||||
final ObjectID<?>[] objects = event.getDispatchableObjects();
|
final ObjectID<?>[] objects = event.event.getDispatchableObjects();
|
||||||
for (ObjectID<?> obj : objects) {
|
for (ObjectID<?> obj : objects) {
|
||||||
if (obj == null)
|
if (obj == null)
|
||||||
continue;
|
continue;
|
||||||
final Set<WorldListener> listeners = getListeners(obj);
|
final Set<WorldListener> listeners = getListeners(obj);
|
||||||
for (final WorldListener listener : listeners) {
|
for (final WorldListener listener : listeners) {
|
||||||
|
if (event.future.isCancelled())
|
||||||
|
return false;
|
||||||
try {
|
try {
|
||||||
if (!listener.dispatch(event))
|
if (!listener.dispatch(event.event))
|
||||||
// remove listener if return value is false
|
// remove listener if return value is false
|
||||||
listeners.remove(listener);
|
listeners.remove(listener);
|
||||||
} catch (ClassCastException e) {
|
} catch (ClassCastException e) {
|
||||||
@@ -89,6 +127,7 @@ public class WorldEventDispatcherImpl implements WorldEventDispatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -134,6 +173,14 @@ public class WorldEventDispatcherImpl implements WorldEventDispatcher {
|
|||||||
listeners.remove(id);
|
listeners.remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link Set} of listeners for an given object. Creates a new one
|
||||||
|
* if does not exists.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* the object id
|
||||||
|
* @return the {@link Set}. Never null.
|
||||||
|
*/
|
||||||
private Set<WorldListener> getListeners(ObjectID<?> id) {
|
private Set<WorldListener> getListeners(ObjectID<?> id) {
|
||||||
Set<WorldListener> set = listeners.get(id);
|
Set<WorldListener> set = listeners.get(id);
|
||||||
if (set == null) {
|
if (set == null) {
|
||||||
@@ -142,4 +189,117 @@ public class WorldEventDispatcherImpl implements WorldEventDispatcher {
|
|||||||
}
|
}
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
timer.cancel();
|
||||||
|
timer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link WorldEventFuture} implementation
|
||||||
|
*
|
||||||
|
* @param <E>
|
||||||
|
* the event type
|
||||||
|
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
||||||
|
*/
|
||||||
|
private static class WorldEventFutureImpl<E extends WorldEvent> extends
|
||||||
|
AbstractFuture<E> implements WorldEventFuture<E> {
|
||||||
|
private boolean running = false;
|
||||||
|
private boolean complete = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected boolean set(WorldEvent value) {
|
||||||
|
running = false;
|
||||||
|
complete = true;
|
||||||
|
return super.set((E) value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean setException(Throwable throwable) {
|
||||||
|
return super.setException(throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||||
|
if (!mayInterruptIfRunning && running)
|
||||||
|
return false;
|
||||||
|
if (complete)
|
||||||
|
return false;
|
||||||
|
return cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void await() throws InterruptedException {
|
||||||
|
try {
|
||||||
|
super.get();
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void await(long timeout, TimeUnit unit)
|
||||||
|
throws InterruptedException, TimeoutException {
|
||||||
|
try {
|
||||||
|
super.get(timeout, unit);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean awaitUninterruptibly() {
|
||||||
|
try {
|
||||||
|
super.get();
|
||||||
|
return true;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
return false;
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean awaitUninterruptibly(long timeout, TimeUnit unit) {
|
||||||
|
try {
|
||||||
|
super.get(timeout, unit);
|
||||||
|
return true;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
return false;
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
return false;
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple container that contains an event and a future
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
||||||
|
*/
|
||||||
|
private static class EventContainer {
|
||||||
|
/**
|
||||||
|
* The event
|
||||||
|
*/
|
||||||
|
private final WorldEvent event;
|
||||||
|
/**
|
||||||
|
* The future
|
||||||
|
*/
|
||||||
|
private final WorldEventFutureImpl<? extends WorldEvent> future;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* the event
|
||||||
|
* @param future
|
||||||
|
* the future
|
||||||
|
*/
|
||||||
|
public EventContainer(WorldEvent event,
|
||||||
|
WorldEventFutureImpl<? extends WorldEvent> future) {
|
||||||
|
this.event = event;
|
||||||
|
this.future = future;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of l2jserver <l2jserver.com>.
|
||||||
|
*
|
||||||
|
* l2jserver is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* l2jserver 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.l2jserver.service.game.world.event;
|
||||||
|
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an {@link Future} for {@link WorldEvent}. This {@link Future} can be
|
||||||
|
* used to receive notifications once an event has been dispatched to all
|
||||||
|
* listeners.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
||||||
|
*/
|
||||||
|
public interface WorldEventFuture<E extends WorldEvent> extends Future<E> {
|
||||||
|
/**
|
||||||
|
* Waits until the event is dispatched to all listeners
|
||||||
|
*
|
||||||
|
* @throws InterruptedException
|
||||||
|
* if the thread has been interrupted while waiting
|
||||||
|
*/
|
||||||
|
void await() throws InterruptedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits until the event is dispatched to all listeners
|
||||||
|
*
|
||||||
|
* @param timeout
|
||||||
|
* the timeout
|
||||||
|
* @param unit
|
||||||
|
* the timeout unit
|
||||||
|
*
|
||||||
|
* @throws InterruptedException
|
||||||
|
* if the thread has been interrupted while waiting
|
||||||
|
* @throws TimeoutException
|
||||||
|
* if timeout was exceeded
|
||||||
|
*/
|
||||||
|
void await(long timeout, TimeUnit unit) throws InterruptedException,
|
||||||
|
TimeoutException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits until the event is dispatched to all listeners
|
||||||
|
*
|
||||||
|
* @return true if execution ended with no error, false otherwise
|
||||||
|
*/
|
||||||
|
boolean awaitUninterruptibly();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits until the event is dispatched to all listeners
|
||||||
|
*
|
||||||
|
* @param timeout
|
||||||
|
* the timeout
|
||||||
|
* @param unit
|
||||||
|
* the timeout unit
|
||||||
|
*
|
||||||
|
* @return true if execution ended with no error, false otherwise. Please
|
||||||
|
* note that false will be returned if the timeout has expired too!
|
||||||
|
*/
|
||||||
|
boolean awaitUninterruptibly(long timeout, TimeUnit unit);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user