All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.higgs.events.Events Maven / Gradle / Ivy

The newest version!
package io.higgs.events;


import io.higgs.core.InvokableMethod;
import io.higgs.core.StaticUtil;
import io.higgs.core.func.Function1;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.local.LocalAddress;
import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalEventLoopGroup;
import org.cliffc.high_scale_lib.NonBlockingHashMap;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * @author Courtney Robinson 
 */
public class Events {
    private static NonBlockingHashMap groups = new NonBlockingHashMap<>();
    protected final LocalAddress address;
    private final EventLoopGroup group;
    private final List registeredFactories = new ArrayList<>();
    protected EventServer server;
    protected NonBlockingHashMap channels = new NonBlockingHashMap<>();

    public Events(String groupName) {
        address = new LocalAddress(groupName);
        group = new LocalEventLoopGroup();
        server = new EventServer(address);
        server.registerMethodProcessor(new EventMethodProcessor());
        server.start();
    }

    public static Events get() {
        return group("*");
    }

    /**
     * Create a new event group with it's own set of resources
     *
     * @param name the name of this group
     * @return a new group if one with the same name doesn't already exist in which case the existing one is returned
     */
    public static Events group(String name) {
        if (name == null) {
            throw new IllegalArgumentException("Group name is required");
        }
        Events group = groups.get(name);
        if (group == null) {
            group = new Events(name);
            groups.put(name, group);
        }
        return group;
    }

    /**
     * Execute a task in the event loop
     *
     * @param task the task
     * @param 
     */
    public  void execute(final Task task) {
        eventLoop().submit(new Runnable() {
            public void run() {
                task.apply();
            }
        });
    }

    /**
     * @return The event loop used to schedule tasks
     */
    public EventLoop eventLoop() {
        return server.channel().eventLoop();
    }

    /**
     * @return the server used for pub-sub events  in this event group
     */
    public EventServer server() {
        return server;
    }

    /**
     * @deprecated
     */
    public  void on(Function1 function, final String... events) {
        Event[] e = new Event[events.length];
        for (int i = 0; i < events.length; i++) {
            final int finalI = i;
            e[i] = new Event() {
                public String name() {
                    return events[finalI];
                }
            };
        }
        on(function, e);
    }

    /**
     * Note that by providing a function its {@link Function1#apply(Object)} method
     * can be invoked from multiple threads
     *
     * @param function the function to invoke for these events
     * @param events   a set of events to subscribe the function to
     * @param       the type the function accepts, only events matching this type will cause invocation
     */
    public  void on(Function1 function, Event... events) {
        if (events.length == 0) {
            throw new IllegalArgumentException("At least one event is required");
        }
        for (Event event : events) {
            server.registerMethod(new FunctionEventMethod<>(event.name(), function));
        }
    }

    /**
     * Subscribe all eligible class's in this package for events
     *
     * @param pkg
     */
    public void subscribe(Package pkg) {
        server.registerPackage(pkg);
    }

    /**
     * Subscribe all eligible class's in this package AND it's sub packages for events
     * An eligible class is any class where at least one of it's methods is annotated with {@link io.higgs.core.method}
     *
     * @param pkg
     */
    public void subscribeAll(Package pkg) {
        server.registerPackageAndSubpackages(pkg);
    }

    public void subscribe(Class klass) {
        server.registerClass(klass);
    }

    /**
     * @param instance an instance to register
     */
    public void subscribe(Object instance) {
        if (instance == null) {
            throw new IllegalArgumentException("cannot register null instance");
        }
        SingletonFactory factory = new SingletonFactory(server, instance);
        registeredFactories.add(factory);
        server.registerObjectFactory(factory);
    }

    /**
     * Unsubscribe all referentially equal subscriptions of the instance provided.
     * If the same instance was subscribed multiple times, ALL  subscriptions will be removed
     * i.e. where subscribedObject == instance ONLY
     * That is to say, even if subscribedObject.equals(instance) would return true
     * an instance is only removed if subscribedObject == instance (ref)
     *
     * @param instance the instance to unsubscribe
     */
    public void unsubscribe(Object instance) {
        for (SingletonFactory f : registeredFactories) {
            if (f.instance() == instance) {
                server.deRegister(f);
            }
        }
    }

    /**
     * Try to use the type safe {@link #emit(Event, Object...)} instead
     *
     * @deprecated
     */
    public ChannelFuture emit(final String event, Object... param) {
        return emit(new Event() {
            @Override
            public String name() {
                return event;
            }
        }, param);
    }

    /**
     * Emit an event with the given name and parameters
     *
     * @param event thhe name of the event
     * @param param one or more parameters to pass to subscribers
     * @return a future which will be notified when the event has finished, been cancelled or had an error
     */
    public ChannelFuture emit(Event event, Object... param) {
        if (event == null || event.name() == null) {
            throw new IllegalArgumentException("event name cannot be null");
        }
        Channel channel = channels.get(event.name());
        //make sure the channel is writable
        if (channel != null && !channel.isActive()) {
            channel = null;
        }
        if (channel == null) {
            channel = bootstrapChannel();
            channels.put(event.name(), channel);
        }
        return StaticUtil.write(channel, new EventMessage(event.name(), param));
    }

    private Channel bootstrapChannel() {
        Bootstrap cb = new Bootstrap();
        cb.group(group)
                .channel(LocalChannel.class)
                .handler(new ChannelInitializer() {
                    @Override
                    public void initChannel(LocalChannel ch) throws Exception {
                        ch.pipeline().addLast(
                                // new LoggingHandler(LogLevel.ERROR),
                                new EventHandler(new LinkedList()));
                    }
                });

        // Start the client.
        return cb.connect(address).syncUninterruptibly().channel();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy