/* * Quasar: lightweight threads and actors for the JVM. * Copyright (c) 2013-2015, Parallel Universe Software Co. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 3.0 * as published by the Free Software Foundation. */ package co.paralleluniverse.actors; import co.paralleluniverse.common.util.Debug; import co.paralleluniverse.common.util.ServiceUtil; import co.paralleluniverse.fibers.DefaultFiberScheduler; import co.paralleluniverse.fibers.FiberFactory; import co.paralleluniverse.fibers.FiberScheduler; import co.paralleluniverse.fibers.SuspendExecution; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A registry used to find registered actors by name. Actors are registered with the {@link Actor#register() } method. * * @author pron */ public class ActorRegistry { private static final Logger LOG = LoggerFactory.getLogger(ActorRegistry.class); private static final co.paralleluniverse.actors.spi.ActorRegistry registry; static { co.paralleluniverse.actors.spi.ActorRegistry tmp = ServiceUtil.loadSingletonServiceOrNull(co.paralleluniverse.actors.spi.ActorRegistry.class); if (tmp == null) tmp = new LocalActorRegistry(); registry = tmp; LOG.info("Actor registry is {}", registry); } static <Message> void register(Actor<Message, ?> actor) throws SuspendExecution { final String name = actor.getName(); if (name == null) throw new IllegalArgumentException("name is null"); LOG.info("Registering {}: {}", name, actor); actor.preRegister(name); registry.register(actor, actor.ref0()); actor.postRegister(); actor.monitor(); } static <Message> void unregister(Actor<Message, ?> actor) { LOG.info("Unregistering actor: {}", actor.getName()); registry.unregister(actor, actor.ref()); } /** * Locates a registered actor by name. * * @param name the actor's name. * @return the actor, or {@code null} if no actor by that name is currently registered. */ public static <T extends ActorRef<?>> T tryGetActor(String name) throws SuspendExecution { return (T) registry.tryGetActor(name); } /** * Locates a registered actor by name, or blocks until one is registered, but no more than the given timeout. * * @param name the actor's name. * @param timeout the timeout * @param unit the timeout's unit * @return the actor, or {@code null} if the timeout expires before one is registered. */ public static <T extends ActorRef<?>> T getActor(String name, long timeout, TimeUnit unit) throws InterruptedException, SuspendExecution { return (T) registry.getActor(name, timeout, unit); } /** * Locates a registered actor by name, or blocks until one is registered. * * @param name the actor's name. * @return the actor. */ public static <T extends ActorRef<?>> T getActor(String name) throws InterruptedException, SuspendExecution { return getActor(name, 0, null); } /** * Locates a registered actor by name, or, if not actor by that name is currently registered, spawns and registers it. * This method atomically checks if an actor by the given name is registers, and if so, returns it; otherwise it spawns and registers the * actor returned by the given factory. * * @param name the actor's name. * @param actorFactory returns an actor that will be registered if one isn't currently registered. * @param scheduler the {@link FiberScheduler} to use when spawning the actor, or {@code null} to spawn the fiber using the default scheduler. * @return the actor. */ public static <Message> ActorRef<Message> getOrRegisterActor(final String name, final Callable<Actor<Message, ?>> actorFactory, final FiberScheduler scheduler) throws SuspendExecution { Callable<ActorRef<Message>> factory = new Callable<ActorRef<Message>>() { @Override public ActorRef<Message> call() throws Exception { Actor actor = actorFactory.call(); actor.preRegister(name); final FiberFactory ff = scheduler; return scheduler != null ? actor.spawn(ff) : actor.spawnThread(); } }; ActorRef<Message> actor = registry.getOrRegisterActor(name, factory); LocalActor.postRegister(actor); return actor; } /** * Locates a registered actor by name, or, if not actor by that name is currently registered, spawns and registers it. * This method atomically checks if an actor by the given name is registers, and if so, returns it; otherwise it spawns the actor * returned by the given factory using the default fiber scheduler, and registers it. * * @param name the actor's name. * @param actorFactory returns an actor that will be registered if one isn't currently registered. * @return the actor. */ public static <Message> ActorRef<Message> getOrRegisterActor(String name, Callable<Actor<Message, ?>> actorFactory) throws SuspendExecution { return getOrRegisterActor(name, actorFactory, DefaultFiberScheduler.getInstance()); } /** * Checks whether the registry is global to the entire cluster. * * @return {@code true} if the registry is global to the entire cluster, or {@code false} if it is local to this JVM instance. */ public static boolean hasGlobalRegistry() { return !(registry instanceof LocalActorRegistry); } /** * Shuts down the registry. */ public static void shutdown() { registry.shutdown(); } /** * Clears the registry (use only in tests!). */ public static void clear() { if (!Debug.isUnitTest()) throw new IllegalStateException("Must only be called in unit tests"); if (registry instanceof LocalActorRegistry) ((LocalActorRegistry) registry).clear(); else throw new UnsupportedOperationException(); } }