Improved concurrency, annotations

v5stable
Ondřej Hruška 10 years ago
parent e2b6752efb
commit 6b69dabbb9
  1. 20
      src/mightypork/rogue/App.java
  2. 2
      src/mightypork/rogue/Config.java
  3. 18
      src/mightypork/rogue/MainLoop.java
  4. 6
      src/mightypork/rogue/bus/events/ActionRequest.java
  5. 6
      src/mightypork/rogue/bus/events/DestroyEvent.java
  6. 4
      src/mightypork/rogue/bus/events/KeyboardEvent.java
  7. 6
      src/mightypork/rogue/bus/events/MainLoopTaskRequest.java
  8. 4
      src/mightypork/rogue/bus/events/MouseButtonEvent.java
  9. 4
      src/mightypork/rogue/bus/events/MouseMotionEvent.java
  10. 6
      src/mightypork/rogue/bus/events/ResourceLoadRequest.java
  11. 9
      src/mightypork/rogue/bus/events/ScreenChangeEvent.java
  12. 6
      src/mightypork/rogue/bus/events/ScreenRequestEvent.java
  13. 7
      src/mightypork/rogue/bus/events/UpdateEvent.java
  14. 2
      src/mightypork/rogue/fonts/FontBank.java
  15. 2
      src/mightypork/rogue/gui/screens/test_bouncyboxes/ScreenTestBouncy.java
  16. 4
      src/mightypork/rogue/gui/screens/test_cat_sound/ScreenTestCat.java
  17. 8
      src/mightypork/rogue/input/InputSystem.java
  18. 7
      src/mightypork/rogue/loading/AsyncResourceLoader.java
  19. 13
      src/mightypork/rogue/loading/BaseDeferredResource.java
  20. 4
      src/mightypork/rogue/render/DisplaySystem.java
  21. 2
      src/mightypork/rogue/sound/DeferredAudio.java
  22. 2
      src/mightypork/rogue/sound/SoundSystem.java
  23. 2
      src/mightypork/rogue/textures/TextureBank.java
  24. 154
      src/mightypork/utils/control/bus/EventBus.java
  25. 100
      src/mightypork/utils/control/bus/EventChannel.java
  26. 2
      src/mightypork/utils/control/bus/events/Event.java
  27. 2
      src/mightypork/utils/control/bus/events/types/SingularEvent.java
  28. 22
      src/mightypork/utils/logging/Log.java
  29. 219
      src/mightypork/utils/logging/LogInstance.java
  30. 16
      src/mightypork/utils/logging/LogMonitor.java
  31. 17
      src/mightypork/utils/logging/LogToSysoutMonitor.java

@ -5,6 +5,7 @@ import java.io.File;
import java.io.RandomAccessFile;
import java.nio.channels.FileLock;
import java.util.ArrayList;
import java.util.logging.Level;
import javax.swing.JOptionPane;
@ -18,9 +19,8 @@ import mightypork.rogue.input.InputSystem;
import mightypork.rogue.input.KeyStroke;
import mightypork.rogue.render.DisplaySystem;
import mightypork.rogue.sound.SoundSystem;
import mightypork.rogue.util.SlickLogRedirector;
import mightypork.utils.control.bus.EventBus;
import mightypork.utils.control.bus.events.DestroyEvent;
import mightypork.utils.control.bus.events.UpdateEvent;
import mightypork.utils.control.interf.Destroyable;
import mightypork.utils.control.interf.Updateable;
import mightypork.utils.logging.Log;
@ -105,8 +105,10 @@ public class App implements AppAccess {
{
Log.i("Shutting down subsystems...");
bus().send(new DestroyEvent());
bus().destroy();
if(bus() != null) {
bus().send(new DestroyEvent());
bus().destroy();
}
Log.i("Terminating...");
System.exit(0);
@ -124,8 +126,11 @@ public class App implements AppAccess {
* Setup logging
*/
final LogInstance log = Log.create("runtime", Paths.LOGS, 10);
log.setFileLevel(Level.WARNING);
log.setSysoutLevel(Level.ALL);
log.enable(Config.LOGGING_ENABLED);
log.enableSysout(Config.LOG_TO_STDOUT);
org.newdawn.slick.util.Log.setLogSystem(new SlickLogRedirector(log));
Log.f1("Initializing subsystems...");
@ -134,7 +139,6 @@ public class App implements AppAccess {
*/
Log.f2("Initializing Event Bus...");
eventBus = new EventBus();
eventBus.enableLogging(Config.LOG_BUS);
initChannels();
/*
@ -236,7 +240,7 @@ public class App implements AppAccess {
@Override
public void run()
{
bus().queue(new ActionRequest(RequestType.FULLSCREEN));
bus().send(new ActionRequest(RequestType.FULLSCREEN));
}
});
@ -246,7 +250,7 @@ public class App implements AppAccess {
@Override
public void run()
{
bus().queue(new ActionRequest(RequestType.SCREENSHOT));
bus().send(new ActionRequest(RequestType.SCREENSHOT));
}
});
@ -256,7 +260,7 @@ public class App implements AppAccess {
@Override
public void run()
{
bus().queue(new ActionRequest(RequestType.SHUTDOWN));
bus().send(new ActionRequest(RequestType.SHUTDOWN));
}
});
}

@ -76,6 +76,4 @@ public class Config {
public static boolean LOG_TO_STDOUT = true;
public static boolean SINGLE_INSTANCE = true;
public static boolean LOG_BUS = false;
}

@ -8,11 +8,11 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import mightypork.rogue.bus.Subsystem;
import mightypork.rogue.bus.events.ActionRequest;
import mightypork.rogue.bus.events.UpdateEvent;
import mightypork.rogue.bus.events.ActionRequest.RequestType;
import mightypork.rogue.bus.events.MainLoopTaskRequest;
import mightypork.rogue.tasks.TaskTakeScreenshot;
import mightypork.rogue.util.Utils;
import mightypork.utils.control.bus.events.UpdateEvent;
import mightypork.utils.control.timing.TimerDelta;
@ -37,18 +37,18 @@ public class MainLoop extends Subsystem implements ActionRequest.Listener, MainL
{
timer = new TimerDelta();
while (running) {
while (running) {
Runnable r;
while ((r = taskQueue.poll()) != null) {
r.run();
}
disp().beginFrame();
bus().send(new UpdateEvent(timer.getDelta()));
for (final Runnable r : regularTasks) {
r.run();
}
Runnable r;
while ((r = taskQueue.poll()) != null) {
r.run();
for (final Runnable r2 : regularTasks) {
r2.run();
}
disp().endFrame();

@ -1,8 +1,9 @@
package mightypork.rogue.bus.events;
import mightypork.utils.control.bus.Event;
import mightypork.utils.control.bus.SingularEvent;
import mightypork.utils.control.bus.events.Event;
import mightypork.utils.control.bus.events.types.QueuedEvent;
import mightypork.utils.control.bus.events.types.SingularEvent;
/**
@ -11,6 +12,7 @@ import mightypork.utils.control.bus.SingularEvent;
* @author MightyPork
*/
@SingularEvent
@QueuedEvent
public class ActionRequest implements Event<ActionRequest.Listener> {
private final RequestType type;

@ -1,7 +1,8 @@
package mightypork.utils.control.bus.events;
package mightypork.rogue.bus.events;
import mightypork.utils.control.bus.Event;
import mightypork.utils.control.bus.events.Event;
import mightypork.utils.control.bus.events.types.ImmediateEvent;
import mightypork.utils.control.interf.Destroyable;
@ -10,6 +11,7 @@ import mightypork.utils.control.interf.Destroyable;
*
* @author MightyPork
*/
@ImmediateEvent
public class DestroyEvent implements Event<Destroyable> {
@Override

@ -1,7 +1,8 @@
package mightypork.rogue.bus.events;
import mightypork.utils.control.bus.Event;
import mightypork.utils.control.bus.events.Event;
import mightypork.utils.control.bus.events.types.QueuedEvent;
import org.lwjgl.input.Keyboard;
@ -11,6 +12,7 @@ import org.lwjgl.input.Keyboard;
*
* @author MightyPork
*/
@QueuedEvent
public class KeyboardEvent implements Event<KeyboardEvent.Listener> {
private final int key;

@ -1,8 +1,9 @@
package mightypork.rogue.bus.events;
import mightypork.utils.control.bus.Event;
import mightypork.utils.control.bus.SingularEvent;
import mightypork.utils.control.bus.events.Event;
import mightypork.utils.control.bus.events.types.QueuedEvent;
import mightypork.utils.control.bus.events.types.SingularEvent;
/**
@ -11,6 +12,7 @@ import mightypork.utils.control.bus.SingularEvent;
* @author MightyPork
*/
@SingularEvent
@QueuedEvent
public class MainLoopTaskRequest implements Event<MainLoopTaskRequest.Listener> {
private final Runnable task;

@ -1,7 +1,8 @@
package mightypork.rogue.bus.events;
import mightypork.utils.control.bus.Event;
import mightypork.utils.control.bus.events.Event;
import mightypork.utils.control.bus.events.types.QueuedEvent;
import mightypork.utils.math.coord.Coord;
@ -10,6 +11,7 @@ import mightypork.utils.math.coord.Coord;
*
* @author MightyPork
*/
@QueuedEvent
public class MouseButtonEvent implements Event<MouseButtonEvent.Listener> {
public static final int BUTTON_LEFT = 0;

@ -1,7 +1,8 @@
package mightypork.rogue.bus.events;
import mightypork.utils.control.bus.Event;
import mightypork.utils.control.bus.events.Event;
import mightypork.utils.control.bus.events.types.QueuedEvent;
import mightypork.utils.math.coord.Coord;
@ -10,6 +11,7 @@ import mightypork.utils.math.coord.Coord;
*
* @author MightyPork
*/
@QueuedEvent
public class MouseMotionEvent implements Event<MouseMotionEvent.Listener> {
private final Coord move;

@ -2,8 +2,9 @@ package mightypork.rogue.bus.events;
import mightypork.rogue.loading.DeferredResource;
import mightypork.utils.control.bus.Event;
import mightypork.utils.control.bus.SingularEvent;
import mightypork.utils.control.bus.events.Event;
import mightypork.utils.control.bus.events.types.QueuedEvent;
import mightypork.utils.control.bus.events.types.SingularEvent;
/**
@ -12,6 +13,7 @@ import mightypork.utils.control.bus.SingularEvent;
* @author MightyPork
*/
@SingularEvent
@QueuedEvent
public class ResourceLoadRequest implements Event<ResourceLoadRequest.Listener> {
private final DeferredResource resource;

@ -1,10 +1,17 @@
package mightypork.rogue.bus.events;
import mightypork.utils.control.bus.Event;
import mightypork.utils.control.bus.events.Event;
import mightypork.utils.control.bus.events.types.QueuedEvent;
import mightypork.utils.math.coord.Coord;
/**
* Screen resolution or mode was changed
*
* @author MightyPork
*/
@QueuedEvent
public class ScreenChangeEvent implements Event<ScreenChangeEvent.Listener> {
private final boolean fullscreen;

@ -1,8 +1,9 @@
package mightypork.rogue.bus.events;
import mightypork.utils.control.bus.Event;
import mightypork.utils.control.bus.SingularEvent;
import mightypork.utils.control.bus.events.Event;
import mightypork.utils.control.bus.events.types.QueuedEvent;
import mightypork.utils.control.bus.events.types.SingularEvent;
/**
@ -11,6 +12,7 @@ import mightypork.utils.control.bus.SingularEvent;
* @author MightyPork
*/
@SingularEvent
@QueuedEvent
public class ScreenRequestEvent implements Event<ScreenRequestEvent.Listener> {
private final String scrName;

@ -1,7 +1,8 @@
package mightypork.utils.control.bus.events;
package mightypork.rogue.bus.events;
import mightypork.utils.control.bus.Event;
import mightypork.utils.control.bus.events.Event;
import mightypork.utils.control.bus.events.types.ImmediateEvent;
import mightypork.utils.control.interf.Updateable;
@ -10,6 +11,8 @@ import mightypork.utils.control.interf.Updateable;
*
* @author MightyPork
*/
// sending via queue would hog the bus
@ImmediateEvent
public class UpdateEvent implements Event<Updateable> {
private final double deltaTime;

@ -36,7 +36,7 @@ public class FontBank extends AppAdapter {
*/
public void loadFont(String key, DeferredFont font)
{
bus().queue(new ResourceLoadRequest(font));
bus().send(new ResourceLoadRequest(font));
fonts.put(key, font);
}

@ -44,7 +44,7 @@ public class ScreenTestBouncy extends LayeredScreen {
@Override
public void run()
{
bus().queue(new ScreenRequestEvent("test.cat"));
bus().send(new ScreenRequestEvent("test.cat"));
}
});
}

@ -28,7 +28,7 @@ public class ScreenTestCat extends LayeredScreen {
public void run()
{
snd().fadeOutAllLoops();
bus().schedule(new ActionRequest(RequestType.SHUTDOWN), 3);
bus().sendDelayed(new ActionRequest(RequestType.SHUTDOWN), 3);
}
});
@ -37,7 +37,7 @@ public class ScreenTestCat extends LayeredScreen {
@Override
public void run()
{
bus().queue(new ScreenRequestEvent("test.bouncy"));
bus().send(new ScreenRequestEvent("test.bouncy"));
}
});
}

@ -92,14 +92,14 @@ public class InputSystem extends Subsystem implements Updateable, KeyBinder {
wasMouse = true;
}
if (wasMouse && !moveSum.isZero()) bus().queue(new MouseMotionEvent(lastPos, moveSum));
if (wasMouse && !moveSum.isZero()) bus().send(new MouseMotionEvent(lastPos, moveSum));
while (Keyboard.next()) {
onKeyEvent();
}
if (Display.isCloseRequested()) {
bus().queue(new ActionRequest(RequestType.SHUTDOWN));
bus().send(new ActionRequest(RequestType.SHUTDOWN));
}
}
@ -118,7 +118,7 @@ public class InputSystem extends Subsystem implements Updateable, KeyBinder {
}
if (button != -1 || wheeld != 0) {
bus().queue(new MouseButtonEvent(pos, button, down, wheeld));
bus().send(new MouseButtonEvent(pos, button, down, wheeld));
}
moveSum.add_ip(move);
@ -132,7 +132,7 @@ public class InputSystem extends Subsystem implements Updateable, KeyBinder {
final boolean down = Keyboard.getEventKeyState();
final char c = Keyboard.getEventCharacter();
bus().queue(new KeyboardEvent(key, c, down));
bus().send(new KeyboardEvent(key, c, down));
}

@ -63,7 +63,10 @@ public class AsyncResourceLoader extends Thread implements ResourceLoadRequest.L
// textures & fonts needs to be loaded in main thread
if (def.getClass().isAnnotationPresent(MustLoadInMainThread.class)) {
app.bus().queue(new MainLoopTaskRequest(new Runnable() {
Log.f3("<LOADER> Loading in main thread:\n "+Log.str(def));
app.bus().send(new MainLoopTaskRequest(new Runnable() {
@Override
public void run()
@ -75,6 +78,8 @@ public class AsyncResourceLoader extends Thread implements ResourceLoadRequest.L
continue;
}
Log.f3("<LOADER> Loading async:\n "+Log.str(def));
exs.submit(new Runnable() {
@Override

@ -27,28 +27,28 @@ public abstract class BaseDeferredResource implements DeferredResource, Destroya
@Override
public synchronized final void load()
{
if (loadAttempted) return;
loadAttempted = true;
loadFailed = false;
if (isNull()) return;
try {
if (resource == null) throw new NullPointerException("Resource string cannot be null for non-null resource.");
Log.f3("<res> Loading: " + this);
Log.f3("<RES> Loading: " + this);
loadResource(resource);
Log.f3("<res> Loaded: " + this + " loaded.");
Log.f3("<RES> Loaded: " + this + " loaded.");
} catch (final Exception e) {
loadFailed = true;
Log.e("Failed to load resource \"" + resource + "\"", e);
Log.e("<RES> Failed to load \"" + resource + "\"", e);
}
loadAttempted = true;
}
@Override
public synchronized final boolean isLoaded()
public final boolean isLoaded()
{
if (isNull()) return false;
@ -68,6 +68,7 @@ public abstract class BaseDeferredResource implements DeferredResource, Destroya
if (isLoaded()) {
return true;
} else {
Log.w("<RES> First use, not loaded yet - loading directly\n"+this);
load();
}

@ -86,7 +86,7 @@ public class DisplaySystem extends Subsystem implements ConstraintContext {
Display.update();
}
bus().queue(new ScreenChangeEvent(true, Display.isFullscreen(), getSize()));
bus().send(new ScreenChangeEvent(true, Display.isFullscreen(), getSize()));
} catch (final Throwable t) {
Log.e("Failed to toggle fullscreen mode.", t);
@ -166,7 +166,7 @@ public class DisplaySystem extends Subsystem implements ConstraintContext {
{
// handle resize
if (Display.wasResized()) {
bus().queue(new ScreenChangeEvent(false, Display.isFullscreen(), getSize()));
bus().send(new ScreenChangeEvent(false, Display.isFullscreen(), getSize()));
}
glLoadIdentity();

@ -225,7 +225,7 @@ public class DeferredAudio extends BaseDeferredResource {
@Override
public void destroy()
{
if (!isLoaded()) return;
if (!isLoaded() || backingAudio == null) return;
backingAudio.release();
backingAudio = null;

@ -149,7 +149,7 @@ public class SoundSystem extends Subsystem implements Updateable {
private DeferredAudio getResource(String res)
{
final DeferredAudio a = new DeferredAudio(res);
bus().queue(new ResourceLoadRequest(a));
bus().send(new ResourceLoadRequest(a));
if (resources.contains(a)) throw new IllegalArgumentException("Sound resource " + res + " is already registered.");
resources.add(a);

@ -58,7 +58,7 @@ public class TextureBank extends AppAdapter {
tx.setFilter(filter_min, filter_mag);
tx.setWrap(wrap);
bus().queue(new ResourceLoadRequest(tx));
bus().send(new ResourceLoadRequest(tx));
textures.put(key, tx);
lastTx = tx;

@ -5,6 +5,10 @@ import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import mightypork.utils.control.bus.events.Event;
import mightypork.utils.control.bus.events.types.ImmediateEvent;
import mightypork.utils.control.bus.events.types.QueuedEvent;
import mightypork.utils.control.bus.events.types.SingularEvent;
import mightypork.utils.control.interf.Destroyable;
import mightypork.utils.logging.Log;
@ -23,17 +27,16 @@ final public class EventBus implements Destroyable {
private final BufferedHashSet<Object> clients = new BufferedHashSet<Object>();
/** Messages queued for delivery */
private final DelayQueue<DelayedMessage> sendQueue = new DelayQueue<DelayedMessage>();
private final DelayQueue<DelayedEvent> sendQueue = new DelayQueue<DelayedEvent>();
/** Queue polling thread */
private final QueuePollingThread busThread;
/** Log all */
private boolean logging = false;
/** Whether the bus was destroyed */
private boolean dead = false;
public boolean logSending = false;
/**
* Make a new bus and start it's queue thread.
@ -44,23 +47,6 @@ final public class EventBus implements Destroyable {
}
/**
* Enable a level of logging.
*
* @param level 0 none, 1 warning only, 2 all
*/
public void enableLogging(boolean level)
{
assertLive();
logging = level;
for (final EventChannel<?, ?> ch : channels) {
ch.enableLogging(logging);
}
}
/**
* Add a {@link EventChannel} to this bus.<br>
* If a channel of matching types is already added, it is returned instead.
@ -81,25 +67,23 @@ final public class EventBus implements Destroyable {
}
channels.add(channel);
channel.enableLogging(logging);
if (logging) Log.f3("<bus> Added chanel: " + Log.str(channel));
return channel;
}
/**
* Add a channel for given message and client type.
* Add a channel for given event and client type.
*
* @param messageClass message type
* @param eventClass event type
* @param clientClass client type
* @return the created channel instance
*/
public <F_EVENT extends Event<F_CLIENT>, F_CLIENT> EventChannel<?, ?> addChannel(Class<F_EVENT> messageClass, Class<F_CLIENT> clientClass)
public <F_EVENT extends Event<F_CLIENT>, F_CLIENT> EventChannel<?, ?> addChannel(Class<F_EVENT> eventClass, Class<F_CLIENT> clientClass)
{
assertLive();
final EventChannel<F_EVENT, F_CLIENT> channel = EventChannel.create(messageClass, clientClass);
final EventChannel<F_EVENT, F_CLIENT> channel = EventChannel.create(eventClass, clientClass);
return addChannel(channel);
}
@ -118,31 +102,54 @@ final public class EventBus implements Destroyable {
/**
* Add message to a queue
* Send based on annotation.
*
* @param message message
* @param event event
*/
public void queue(Event<?> message)
public void send(Event<?> event)
{
assertLive();
schedule(message, 0);
if(event.getClass().isAnnotationPresent(QueuedEvent.class)) {
sendQueued(event);
return;
}
if(event.getClass().isAnnotationPresent(ImmediateEvent.class)) {
sendDirect(event);
return;
}
dispatch(event);
}
/**
* Add message to a queue, scheduled for given time.
* Add event to a queue
*
* @param message message
* @param delay delay before message is dispatched
* @param event event
*/
public void schedule(Event<?> message, double delay)
public void sendQueued(Event<?> event)
{
assertLive();
final DelayedMessage dm = new DelayedMessage(delay, message);
sendDelayed(event, 0);
}
/**
* Add event to a queue, scheduled for given time.
*
* @param event event
* @param delay delay before event is dispatched
*/
public void sendDelayed(Event<?> event, double delay)
{
assertLive();
final DelayedEvent dm = new DelayedEvent(delay, event);
if (logging) Log.f3("<bus> + [ Queuing: " + Log.str(message) + " ]");
if(logSending) Log.f3("<bus> Q "+Log.str(event)+", t = +"+delay+"s");
sendQueue.add(dm);
}
@ -153,9 +160,26 @@ final public class EventBus implements Destroyable {
* Should be used for real-time events that require immediate response, such
* as timing events.
*
* @param message message
* @param event event
*/
public void send(Event<?> message)
public void sendDirect(Event<?> event)
{
assertLive();
if(logSending) Log.f3("<bus> D "+Log.str(event));
dispatch(event);
}
/**
* Send immediately.<br>
* Should be used for real-time events that require immediate response, such
* as timing events.
*
* @param event event
*/
private void dispatch(Event<?> event)
{
assertLive();
@ -163,29 +187,21 @@ final public class EventBus implements Destroyable {
channels.setBuffering(true);
clients.setBuffering(true);
if (logging) Log.f3("<bus> - [ Sending: " + Log.str(message) + " ]");
boolean sent = false;
boolean channelAccepted = false;
boolean accepted = false;
final boolean singular = message.getClass().isAnnotationPresent(SingularEvent.class);
final boolean singular = event.getClass().isAnnotationPresent(SingularEvent.class);
for (final EventChannel<?, ?> b : channels) {
if (b.canBroadcast(message)) {
channelAccepted = true;
sent |= b.broadcast(message, clients);
if (b.canBroadcast(event)) {
accepted = true;
sent |= b.broadcast(event, clients);
}
if (sent && singular) {
break;
}
if (sent && singular) break;
}
// more severe
if (!channelAccepted) Log.w("<bus> Not accepted by any channel: " + Log.str(message));
// less severe
if (logging && !sent) Log.w("<bus> Not delivered to any client: " + Log.str(message));
if(!accepted) Log.e("<bus> Not accepted by any channel: " + Log.str(event));
channels.setBuffering(false);
clients.setBuffering(false);
@ -206,8 +222,6 @@ final public class EventBus implements Destroyable {
if (client == null) return;
clients.add(client);
if (logging) Log.f3("<bus> ADDING CLIENT " + client);
}
@ -220,9 +234,7 @@ final public class EventBus implements Destroyable {
{
assertLive();
clients.remove(client);
if (logging) Log.f3("<bus> REMOVING CLIENT " + client);
clients.remove(client);
}
@ -241,16 +253,16 @@ final public class EventBus implements Destroyable {
return false;
}
private class DelayedMessage implements Delayed {
private class DelayedEvent implements Delayed {
private final long due;
private Event<?> theMessage = null;
private Event<?> evt = null;
public DelayedMessage(double seconds, Event<?> theMessage) {
public DelayedEvent(double seconds, Event<?> event) {
super();
this.due = System.currentTimeMillis() + (long) (seconds * 1000);
this.theMessage = theMessage;
this.evt = event;
}
@ -268,9 +280,9 @@ final public class EventBus implements Destroyable {
}
public Event<?> getMessage()
public Event<?> getEvent()
{
return theMessage;
return evt;
}
}
@ -288,19 +300,19 @@ final public class EventBus implements Destroyable {
@Override
public void run()
{
DelayedMessage dm;
DelayedEvent evt;
while (!stopped) {
dm = null;
evt = null;
try {
dm = sendQueue.take();
evt = sendQueue.take();
} catch (final InterruptedException ignored) {
//
}
if (dm != null) {
send(dm.getMessage());
if (evt != null) {
dispatch(evt.getEvent());
}
}
}

@ -6,79 +6,69 @@ import java.util.HashSet;
import mightypork.utils.control.bus.clients.DelegatingClient;
import mightypork.utils.control.bus.clients.ToggleableClient;
import mightypork.utils.control.bus.events.Event;
import mightypork.utils.control.bus.events.types.SingularEvent;
import mightypork.utils.logging.Log;
/**
* Message channel, module of {@link EventBus}
* Event delivery channel, module of {@link EventBus}
*
* @author MightyPork
* @param <EVENT> message type
* @param <EVENT> event type
* @param <CLIENT> client (subscriber) type
*/
final public class EventChannel<EVENT extends Event<CLIENT>, CLIENT> {
private final Class<CLIENT> clientClass;
private final Class<EVENT> messageClass;
private boolean logging = false;
private final Class<EVENT> eventClass;
/**
* Create a channel
*
* @param messageClass event class
* @param eventClass event class
* @param clientClass client class
*/
public EventChannel(Class<EVENT> messageClass, Class<CLIENT> clientClass) {
public EventChannel(Class<EVENT> eventClass, Class<CLIENT> clientClass) {
if (messageClass == null || clientClass == null) throw new NullPointerException("Null Message or Client class.");
if (eventClass == null || clientClass == null) {
throw new NullPointerException("Null Event or Client class.");
}
this.clientClass = clientClass;
this.messageClass = messageClass;
this.eventClass = eventClass;
}
/**
* Enable logging of non-warning debug messages.
* Try to broadcast a event.<br>
* If event is of wrong type, <code>false</code> is returned.
*
* @param logging enable logging
*/
public void enableLogging(boolean logging)
{
this.logging = logging;
}
/**
* Try to broadcast a message.<br>
* If message is of wrong type, <code>false</code> is returned.
*
* @param message a message to be sent
* @param event a event to be sent
* @param clients collection of clients
* @return true if message was sent
* @return true if event was sent
*/
public boolean broadcast(Event<?> message, Collection<Object> clients)
public boolean broadcast(Event<?> event, Collection<Object> clients)
{
if (!canBroadcast(message)) return false;
final EVENT evt = messageClass.cast(message);
return doBroadcast(evt, clients, new HashSet<Object>());
if (!canBroadcast(event)) return false;
return doBroadcast(eventClass.cast(event), clients, new HashSet<Object>());
}
/**
* Send the message
* Send the event
*
* @param message sent message
* @param event sent event
* @param clients subscribing clients
* @param processed clients already processed
* @return success
*/
private boolean doBroadcast(final EVENT message, final Collection<Object> clients, final Collection<Object> processed)
private boolean doBroadcast(final EVENT event, final Collection<Object> clients, final Collection<Object> processed)
{
boolean sent = false;
final boolean singular = message.getClass().isAnnotationPresent(SingularEvent.class);
final boolean singular = event.getClass().isAnnotationPresent(SingularEvent.class);
for (final Object client : clients) {
@ -96,13 +86,10 @@ final public class EventChannel<EVENT extends Event<CLIENT>, CLIENT> {
// opt-out
if (client instanceof ToggleableClient) {
if (!((ToggleableClient) client).isListening()) {
if (logging) Log.f3("<bus> Client disabled: " + Log.str(client));
continue;
}
if (!((ToggleableClient) client).isListening()) continue;
}
sent |= sendTo(client, message);
sent |= sendTo(client, event);
// singular event ain't no whore, handled once only.
if (sent && singular) return true;
@ -114,11 +101,9 @@ final public class EventChannel<EVENT extends Event<CLIENT>, CLIENT> {
final Collection<Object> children = ((DelegatingClient) client).getChildClients();
if (children != null && children.size() > 0) {
sent |= doBroadcast(message, children, processed);
sent |= doBroadcast(event, children, processed);
}
} else {
if (logging) Log.f3("<bus> Client not delegating: " + Log.str(client));
}
}
}
@ -128,18 +113,17 @@ final public class EventChannel<EVENT extends Event<CLIENT>, CLIENT> {
/**
* Send a message to a client.
* Send an event to a client.
*
* @param client target client
* @param message message to send
* @param event event to send
* @return success
*/
@SuppressWarnings("unchecked")
private boolean sendTo(Object client, EVENT message)
private boolean sendTo(Object client, EVENT event)
{
if (isClientOfType(client)) {
if (logging) Log.f3("<bus> Delivered " + Log.str(message) + " to " + Log.str(client));
((Event<CLIENT>) message).handleBy((CLIENT) client);
((Event<CLIENT>) event).handleBy((CLIENT) client);
return true;
}
return false;
@ -147,28 +131,28 @@ final public class EventChannel<EVENT extends Event<CLIENT>, CLIENT> {
/**
* Check if the given message can be broadcasted by this
* Check if the given event can be broadcasted by this
* {@link EventChannel}
*
* @param message event object
* @param event event object
* @return can be broadcasted
*/
public boolean canBroadcast(Event<?> message)
public boolean canBroadcast(Event<?> event)
{
return message != null && messageClass.isInstance(message);
return event != null && eventClass.isInstance(event);
}
/**
* Create an instance for given types
*
* @param messageClass event class
* @param eventClass event class
* @param clientClass client class
* @return the broadcaster
*/
public static <F_EVENT extends Event<F_CLIENT>, F_CLIENT> EventChannel<F_EVENT, F_CLIENT> create(Class<F_EVENT> messageClass, Class<F_CLIENT> clientClass)
public static <F_EVENT extends Event<F_CLIENT>, F_CLIENT> EventChannel<F_EVENT, F_CLIENT> create(Class<F_EVENT> eventClass, Class<F_CLIENT> clientClass)
{
return new EventChannel<F_EVENT, F_CLIENT>(messageClass, clientClass);
return new EventChannel<F_EVENT, F_CLIENT>(eventClass, clientClass);
}
@ -202,7 +186,7 @@ final public class EventChannel<EVENT extends Event<CLIENT>, CLIENT> {
final int prime = 13;
int result = 1;
result = prime * result + ((clientClass == null) ? 0 : clientClass.hashCode());
result = prime * result + ((messageClass == null) ? 0 : messageClass.hashCode());
result = prime * result + ((eventClass == null) ? 0 : eventClass.hashCode());
return result;
}
@ -217,9 +201,9 @@ final public class EventChannel<EVENT extends Event<CLIENT>, CLIENT> {
if (clientClass == null) {
if (other.clientClass != null) return false;
} else if (!clientClass.equals(other.clientClass)) return false;
if (messageClass == null) {
if (other.messageClass != null) return false;
} else if (!messageClass.equals(other.messageClass)) return false;
if (eventClass == null) {
if (other.eventClass != null) return false;
} else if (!eventClass.equals(other.eventClass)) return false;
return true;
}
@ -227,6 +211,6 @@ final public class EventChannel<EVENT extends Event<CLIENT>, CLIENT> {
@Override
public String toString()
{
return "{ " + Log.str(messageClass) + " => " + Log.str(clientClass) + " }";
return "{ " + Log.str(eventClass) + " => " + Log.str(clientClass) + " }";
}
}

@ -1,4 +1,4 @@
package mightypork.utils.control.bus;
package mightypork.utils.control.bus.events;
/**

@ -1,4 +1,4 @@
package mightypork.utils.control.bus;
package mightypork.utils.control.bus.events.types;
import java.lang.annotation.*;

@ -8,7 +8,7 @@ import java.util.HashMap;
public class Log {
/** enable static logging */
private static boolean esl = true;
private static boolean staticLogging = true;
/**
@ -18,7 +18,7 @@ public class Log {
*/
public static void f1(String msg)
{
if (esl && ready()) main.f1(msg);
if (staticLogging && ready()) main.f1(msg);
}
@ -29,7 +29,7 @@ public class Log {
*/
public static void f2(String msg)
{
if (esl && ready()) main.f2(msg);
if (staticLogging && ready()) main.f2(msg);
}
@ -40,7 +40,7 @@ public class Log {
*/
public static void f3(String msg)
{
if (esl && ready()) main.f3(msg);
if (staticLogging && ready()) main.f3(msg);
}
@ -51,7 +51,7 @@ public class Log {
*/
public static void i(String msg)
{
if (esl && ready()) main.i(msg);
if (staticLogging && ready()) main.i(msg);
}
@ -62,7 +62,7 @@ public class Log {
*/
public static void w(String msg)
{
if (esl && ready()) main.w(msg);
if (staticLogging && ready()) main.w(msg);
}
@ -73,7 +73,7 @@ public class Log {
*/
public static void e(String msg)
{
if (esl && ready()) main.e(msg);
if (staticLogging && ready()) main.e(msg);
}
@ -85,7 +85,7 @@ public class Log {
*/
public static void e(String msg, Throwable thrown)
{
if (esl && ready()) main.e(msg, thrown);
if (staticLogging && ready()) main.e(msg, thrown);
}
@ -96,13 +96,13 @@ public class Log {
*/
public static void e(Throwable thrown)
{
if (esl && ready()) main.e(thrown);
if (staticLogging && ready()) main.e(thrown);
}
public static void enable(boolean flag)
{
if (esl && ready()) main.enable(flag);
if (staticLogging && ready()) main.enable(flag);
}
@ -113,7 +113,7 @@ public class Log {
*/
public static void enableStaticLogging(boolean flag)
{
esl = flag;
staticLogging = flag;
}
private static HashMap<String, LogInstance> logs = new HashMap<String, LogInstance>();

@ -38,7 +38,7 @@ public class LogInstance {
private final int logs_to_keep;
/** Logs dir */
private final File dir;
private final File log_dir;
/** Logger instance. */
private Logger logger;
@ -54,10 +54,17 @@ public class LogInstance {
private LogToSysoutMonitor sysoutMonitor;
/**
* Log
*
* @param name log name
* @param dir log directory
* @param oldLogCount number of old log files to keep: -1 all, 0 none.
*/
public LogInstance(String name, File dir, int oldLogCount) {
this.name = name;
this.file = new File(dir, name + getSuffix());
this.dir = dir;
this.log_dir = dir;
this.logs_to_keep = oldLogCount;
init();
@ -71,7 +78,7 @@ public class LogInstance {
{
logger = Logger.getLogger(name);
cleanup();
cleanLoggingDirectory();
FileHandler handler = null;
@ -92,26 +99,27 @@ public class LogInstance {
logger.setUseParentHandlers(false);
logger.setLevel(Level.ALL);
logger.info("Main logger initialized.");
logger.info("Logger \""+name+"\" initialized.");
logger.info((new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()));
}
private void cleanup()
private void cleanLoggingDirectory()
{
if (logs_to_keep == 0) return; // overwrite
// move old file
for (final File f : FileUtils.listDirectory(file.getParentFile())) {
if (!f.isFile()) continue;
if (f.equals(file)) {
final Date d = new Date(f.lastModified());
final Date d = new Date(f.lastModified());
final String fbase = name + '_' + (new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss")).format(d);
final String suff = getSuffix();
String cntStr = "";
File f2;
for (int cnt = 0; (f2 = new File(dir, fbase + cntStr + suff)).exists(); cntStr = "_" + (++cnt)) {}
for (int cnt = 0; (f2 = new File(log_dir, fbase + cntStr + suff)).exists(); cntStr = "_" + (++cnt)) {}
f.renameTo(f2);
}
@ -119,7 +127,7 @@ public class LogInstance {
if (logs_to_keep == -1) return; // keep all
final List<File> oldLogs = FileUtils.listDirectory(dir, new FileFilter() {
final List<File> oldLogs = FileUtils.listDirectory(log_dir, new FileFilter() {
@Override
public boolean accept(File f)
@ -174,6 +182,18 @@ public class LogInstance {
}
public void setSysoutLevel(Level level)
{
sysoutMonitor.setLevel(level);
}
public void setFileLevel(Level level)
{
logger.setLevel(level);
}
/**
* Enable logging.
*
@ -197,6 +217,34 @@ public class LogInstance {
}
public void log(Level level, String msg)
{
if (enabled) {
logger.log(level, msg);
String fmt = formatMessage(level, msg, null);
for (final LogMonitor mon : monitors.values()) {
mon.onMessageLogged(level, fmt);
}
}
}
public void log(Level level, String msg, Throwable t)
{
if (enabled) {
logger.log(level, msg, t);
String fmt = formatMessage(level, msg, null);
for (final LogMonitor mon : monitors.values()) {
mon.onMessageLogged(level, fmt);
}
}
}
/**
* Log FINE message
*
@ -204,7 +252,7 @@ public class LogInstance {
*/
public void f1(String msg)
{
if (enabled) logger.log(Level.FINE, msg);
log(Level.FINE, msg);
}
@ -215,7 +263,7 @@ public class LogInstance {
*/
public void f2(String msg)
{
if (enabled) logger.log(Level.FINER, msg);
log(Level.FINER, msg);
}
@ -226,7 +274,7 @@ public class LogInstance {
*/
public void f3(String msg)
{
if (enabled) logger.log(Level.FINEST, msg);
log(Level.FINEST, msg);
}
@ -237,7 +285,7 @@ public class LogInstance {
*/
public void i(String msg)
{
if (enabled) logger.log(Level.INFO, msg);
log(Level.INFO, msg);
}
@ -248,7 +296,7 @@ public class LogInstance {
*/
public void w(String msg)
{
if (enabled) logger.log(Level.WARNING, msg);
log(Level.WARNING, msg);
}
@ -259,7 +307,7 @@ public class LogInstance {
*/
public void e(String msg)
{
if (enabled) logger.log(Level.SEVERE, msg);
log(Level.SEVERE, msg);
}
@ -271,7 +319,7 @@ public class LogInstance {
*/
public void e(String msg, Throwable thrown)
{
if (enabled) logger.log(Level.SEVERE, msg + "\n" + getStackTrace(thrown));
log(Level.SEVERE, msg);
}
@ -282,24 +330,7 @@ public class LogInstance {
*/
public void e(Throwable thrown)
{
if (enabled) logger.log(Level.SEVERE, getStackTrace(thrown));
}
/**
* Get stack trace from throwable
*
* @param t
* @return trace
*/
private static String getStackTrace(Throwable t)
{
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw, true);
t.printStackTrace(pw);
pw.flush();
sw.flush();
return sw.toString();
log(Level.SEVERE, null, thrown);
}
/**
@ -310,74 +341,10 @@ public class LogInstance {
*/
private class LogFormatter extends Formatter {
/** Newline string constant */
private final String nl = System.getProperty("line.separator");
@Override
public String format(LogRecord record)
{
final StringBuffer buf = new StringBuffer(180);
if (record.getMessage().equals("\n")) {
return nl;
}
if (record.getMessage().charAt(0) == '\n') {
buf.append(nl);
record.setMessage(record.getMessage().substring(1));
}
final Level level = record.getLevel();
String trail = "[ ? ]";
if (level == Level.FINE) {
trail = "[ # ] ";
}
if (level == Level.FINER) {
trail = "[ - ] ";
}
if (level == Level.FINEST) {
trail = "[ ] ";
}
if (level == Level.INFO) {
trail = "[ i ] ";
}
if (level == Level.SEVERE) {
trail = "[!E!] ";
}
if (level == Level.WARNING) {
trail = "[!W!] ";
}
record.setMessage(record.getMessage().replaceAll("\n", nl + trail));
buf.append(trail);
buf.append(formatMessage(record));
buf.append(nl);
final Throwable throwable = record.getThrown();
if (throwable != null) {
buf.append("at ");
buf.append(record.getSourceClassName());
buf.append('.');
buf.append(record.getSourceMethodName());
buf.append(nl);
final StringWriter sink = new StringWriter();
throwable.printStackTrace(new PrintWriter(sink, true));
buf.append(sink.toString());
buf.append(nl);
}
final String str = buf.toString();
for (final LogMonitor mon : monitors.values()) {
mon.log(level, str);
}
return str;
return LogInstance.formatMessage(record.getLevel(), record.getMessage(), record.getThrown());
}
}
@ -389,4 +356,60 @@ public class LogInstance {
{
return ".log";
}
private static String formatMessage(Level level, String message, Throwable throwable)
{
final String nl = System.getProperty("line.separator");
if (message.equals("\n")) {
return nl;
}
if (message.charAt(0) == '\n') {
message = nl + message.substring(1);
}
String prefix = "[ ? ]";
if (level == Level.FINE) {
prefix = "[ # ] ";
} else if (level == Level.FINER) {
prefix = "[ - ] ";
} else if (level == Level.FINEST) {
prefix = "[ ] ";
} else if (level == Level.INFO) {
prefix = "[ i ] ";
} else if (level == Level.SEVERE) {
prefix = "[!E!] ";
} else if (level == Level.WARNING) {
prefix = "[!W!] ";
}
message = prefix + message.replaceAll("\n", nl + prefix) + nl;
if (throwable != null) {
message += getStackTrace(throwable);
}
return message;
}
/**
* Get stack trace from throwable
*
* @param t
* @return trace
*/
private static String getStackTrace(Throwable t)
{
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw, true);
t.printStackTrace(pw);
pw.flush();
sw.flush();
return sw.toString();
}
}

@ -4,10 +4,18 @@ package mightypork.utils.logging;
import java.util.logging.Level;
/**
* Log monitor, receives all logged messages
*
* @author MightyPork
*/
public interface LogMonitor {
public void log(Level level, String message);
public void enable(boolean enable);
/**
* Message logged;
*
* @param level message level
* @param message message text, already formatted.
*/
void onMessageLogged(Level level, String message);
}

@ -7,22 +7,29 @@ import java.util.logging.Level;
public class LogToSysoutMonitor implements LogMonitor {
private boolean enabled = true;
private Level accepted = Level.ALL;
public void setLevel(Level level)
{
this.accepted = level;
}
@Override
public void log(Level level, String message)
public void onMessageLogged(Level level, String message)
{
if (!enabled) return;
if (accepted.intValue() > level.intValue()) return;
if (level == Level.FINE || level == Level.FINER || level == Level.FINEST || level == Level.INFO) {
System.out.print(message);
} else if (level == Level.SEVERE || level == Level.WARNING) {
if (level == Level.SEVERE || level == Level.WARNING) {
System.err.print(message);
} else {
System.out.print(message);
}
}
@Override
public void enable(boolean enable)
{
this.enabled = enable;

Loading…
Cancel
Save