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

org.javabits.yar.guice.SimpleRegistry Maven / Gradle / Ivy

/*
 * Copyright 2013 Romain Gilles
 *
 *    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.javabits.yar.guice;

import com.google.common.base.*;
import com.google.common.collect.*;
import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import org.javabits.yar.*;
import org.javabits.yar.Supplier;

import javax.annotation.Nullable;
import java.lang.InterruptedException;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.*;
import java.util.logging.Level;
import java.util.logging.Logger;

import static com.google.common.collect.Lists.transform;
import static com.google.common.util.concurrent.Futures.getUnchecked;
import static java.util.Collections.singletonList;
import static java.util.Collections.unmodifiableSet;
import static java.util.Objects.requireNonNull;
import static org.javabits.yar.InterruptedException.newInterruptedException;
import static org.javabits.yar.guice.GuiceWatchableRegistrationContainer.newLoadingCacheGuiceWatchableRegistrationContainer;
import static org.javabits.yar.guice.GuiceWatchableRegistrationContainer.newMultimapGuiceWatchableRegistrationContainer;
import static org.javabits.yar.guice.WatcherRegistration.newWatcherRegistration;

/**
 * TODO comment
 * TODO implement it with watchers !!!
 * TODO have a look to the equinox implementation
 * Date: 2/10/13
 * Time: 10:47 PM
 *
 * @author Romain Gilles
 */
class SimpleRegistry implements Registry, RegistryHook, InternalRegistry {
    private final LinkedBlockingQueue registryActionQueue;
    private final WatchableRegistrationContainer registrationContainer;
    private final FinalizableReferenceQueue referenceQueue;
    private final long defaultTimeOut;
    private final TimeUnit defaultTimeoutUnit;

    public SimpleRegistry() {
        this(new GuiceWatchableRegistrationContainer());
    }

    SimpleRegistry(WatchableRegistrationContainer registrationContainer) {
        this(registrationContainer, Registry.DEFAULT_TIMEOUT, Registry.DEFAULT_TIME_UNIT);
    }

    SimpleRegistry(WatchableRegistrationContainer registrationContainer, long timeout, TimeUnit unit) {
        referenceQueue = new FinalizableReferenceQueue();
        this.registrationContainer = registrationContainer;
        //TODO test performance of the linked vs. array or any other blocking. may provide a new builder feature to setup the size of the queue
        registryActionQueue = new LinkedBlockingQueue<>();
        Thread registryActionThread = new Thread(new RegistryActionHandler(registryActionQueue), "yar-action-handler");
        registryActionThread.setDaemon(true);
        registryActionThread.start();
        this.defaultTimeOut = timeout;
        this.defaultTimeoutUnit = unit;
    }


    public long defaultTimeout() {
        return defaultTimeOut;
    }

    public TimeUnit defaultTimeUnit() {
        return defaultTimeoutUnit;
    }

    @Override
    public Set types() {
        return unmodifiableSet(registrationContainer.types());
    }

    @Override
    public Set> ids() {
        ImmutableSet.Builder> builder = ImmutableSet.builder();
        for (Type type : registrationContainer.types()) {
            builder.addAll(Lists.transform(getAll(TypeToken.of(type)), new Function, Id>() {
                @Nullable
                @Override
                public Id apply(@Nullable Supplier supplier) {
                    requireNonNull(supplier, "supplier");
                    //noinspection ConstantConditions
                    return supplier.id();
                }
            }));
        }
        return builder.build();
    }

    @Override
    public  List> getAll(Class type) {
        return viewOfEntries(registrationContainer.getAll(type));
    }

    private static  List> viewOfEntries(List> pairs) {
        return transform(pairs, new Function, Supplier>() {

            @Nullable
            @Override
            @SuppressWarnings("unchecked")
            public Supplier apply(@Nullable SupplierRegistration registration) {
                return (Supplier) requireNonNull(registration, "registration").right();
            }
        });
    }

    @Override
    public  List> getAll(Id id) {
        List> pairs = registrationContainer.getAll(id);
        return transformToSuppliers(pairs);

    }

    @Override
    public  List> getAll(TypeToken typeToken) {
        return viewOfEntries(registrationContainer.getAll(typeToken.getType()));
    }


    private static  ImmutableList> transformToSuppliers(List> pairs) {
        return ImmutableList.copyOf(transform(pairs, new Function, Supplier>() {
            @Nullable
            @Override
            @SuppressWarnings("unchecked")
            public Supplier apply(@Nullable SupplierRegistration registration) {
                return requireNonNull(registration, "registration").right();
            }
        }));
    }

    @Nullable
    @Override
    @SuppressWarnings("unchecked")
    public  Supplier get(Class type) {
        SupplierRegistration registration = registrationContainer.getFirst(type);
        if (registration == null) {
            return null;
        }
        return (Supplier) registration.right();
    }

    @Nullable
    @Override
    @SuppressWarnings("unchecked")
    public  Supplier get(TypeToken type) {
        SupplierRegistration registration = registrationContainer.getFirst(type.getType());
        if (registration == null) {
            return null;
        }
        return (Supplier) registration.right();
    }

    @Nullable
    @Override
    @SuppressWarnings("unchecked")
    public  Supplier get(Id id) {
        return getDirectly(id);
    }

    public final  Supplier getDirectly(Id id) {
        SupplierRegistration registration = registrationContainer.getFirst(id);
        if (registration == null) {
            return null;
        }
        return registration.right();
    }

    private  Registration put(Id id, Supplier supplier) {
        checkKey(id, "id");
        checkSupplier(supplier);
        SupplierRegistration registration = new SupplierRegistration<>(id, supplier);
        Add add = new Add<>(registration);
        return executeActionOnRegistry(add);
    }

    @Override
    public  Registration put(Id id, com.google.common.base.Supplier supplier) {
        return put(id, requireNonNull(newSupplierAdapter(id, supplier), "supplier"));
    }

    private  GuavaSupplierAdapter newSupplierAdapter(Id id, com.google.common.base.Supplier supplier) {
        return new GuavaSupplierAdapter<>(id, supplier);
    }

    private  Id checkKey(Id watchedId, String attribute) {
        return requireNonNull(watchedId, attribute);
        //TODO test equals to Ids.IdImpl
    }

    private  void checkSupplier(Supplier supplier) {
        requireNonNull(supplier, "supplier");
    }

    @Override
    public void remove(org.javabits.yar.Registration registration) {
        removeAll(singletonList(registration));
    }

    @Override
    public void removeAll(Collection> registrations) {
        Collection> supplierRegistrations = Collections2.transform(registrations, new Function, SupplierRegistration>() {
            @Nullable
            @Override
            public SupplierRegistration apply(@Nullable Registration registration) {
                return checkSupplierRegistration(registration);
            }
        });
        RegistryAction action = new Remove(supplierRegistrations);
        executeActionOnRegistry(action);
    }

    private  T executeActionOnRegistry(RegistryAction action) {
        try {
            registryActionQueue.put(action);
            return getUnchecked(action.asFuture());
        } catch (InterruptedException e) {
            throw newInterruptedException(String.format("Cannot execute action [%s] on the registry", action), e);
        }
    }

    private SupplierRegistration checkSupplierRegistration(org.javabits.yar.Registration registration) {
        return checkRegistration(registration, SupplierRegistration.class);
    }

    private  T checkRegistration(Registration registration, Class registrationClass) {
        requireNonNull(registration, "registration");

        if (!(registrationClass.isInstance(registration))) {
            throw new IllegalArgumentException(String.format("Only %s registration class are supported", registrationClass.getName()));
        }
        return registrationClass.cast(registration);
    }

    @Override
    public  Registration addWatcher(IdMatcher idMatcher, Watcher watcher) {
        checkKeyMatcher(idMatcher, "idMatcher");
        WatcherRegistration watcherRegistration = newWatcherRegistration(idMatcher, watcher, referenceQueue, this);
        return addWatcherRegistration(watcherRegistration);
    }

     Registration addWatcherRegistration(WatcherRegistration watcherRegistration) {
        return executeActionOnRegistry(new AddWatcher<>(watcherRegistration));
    }

    public  Registration addSupplierListener(IdMatcher idMatcher, SupplierListener supplierListener) {
        checkKeyMatcher(idMatcher, "idMatcher");
        requireNonNull(supplierListener, "supplierListener");
        WatcherRegistration watcherRegistration = newWatcherRegistration(idMatcher, supplierListener, referenceQueue, this);
        return addWatcherRegistration(watcherRegistration);
    }

    private  IdMatcher checkKeyMatcher(IdMatcher matcher, String attribute) {
        return requireNonNull(matcher, attribute);
    }

    @Override
    public void removeWatcher(Registration watcherRegistration) {
        removeAllWatchers(Collections.singletonList(watcherRegistration));
    }

    @Override
    public void removeAllWatchers(Collection> watcherRegistrations) {
        Collection> registrations = Collections2.transform(watcherRegistrations, new Function, WatcherRegistration>() {
            @Nullable
            @Override
            public WatcherRegistration apply(@Nullable Registration watcherRegistration) {
                return checkRegistration(watcherRegistration, WatcherRegistration.class);
            }
        });
        RemoveWatcher action = new RemoveWatcher(registrations);
        executeActionOnRegistry(action);
    }

    @Override
    public void invalidate(Type type) {
        invalidateAll(singletonList(type));
    }

    @Override
    public void invalidateAll(Collection types) {
        RegistryAction removeAllAction = new InvalidateType(types);
        executeActionOnRegistry(removeAllAction);
    }

    @Override
    public void addTypeListener(TypeListener typeListener) {
        registrationContainer.addTypeListener(typeListener);
    }

    @Override
    public void removeTypeListener(TypeListener typeListener) {
        registrationContainer.removeTypeListener(typeListener);
    }

    @Override
    public boolean hasPendingListenerUpdateTasks() {
        return registrationContainer.hasPendingListenerUpdateTasks();
    }

    @Override
    public void addEndOfListenerUpdateTasksListener(EndOfListenerUpdateTasksListener listener) {
        registrationContainer.addEndOfListenerUpdateTasksListener(listener);
    }

    static interface RegistryAction {

        void execute();

        ListenableFuture asFuture();

    }

    static abstract class AbstractRegistryAction implements RegistryAction {
        private final SettableFuture future = SettableFuture.create();

        @Override
        public final void execute() {
            try {
                T result = doExecute();
                future.set(result);
            } catch (Exception e) {
                future.setException(e);
            }
        }

        abstract T doExecute() throws Exception;

        @Override
        public final ListenableFuture asFuture() {
            return future;
        }
    }

    private class Add extends AbstractRegistryAction> {
        private final SupplierRegistration registration;

        Add(SupplierRegistration registration) {
            this.registration = registration;
        }

        @Override
        Registration doExecute() throws Exception {
            registrationContainer.put(registration, defaultTimeOut, defaultTimeoutUnit);
            return registration;
        }

    }

    private class Remove extends AbstractRegistryAction {
        private final Iterable> registrations;

        Remove(Iterable> registrations) {
            this.registrations = registrations;
        }

        @Override
        Void doExecute() throws Exception {
            for (SupplierRegistration registration : registrations) {
                registrationContainer.remove(registration, defaultTimeOut, defaultTimeoutUnit);
            }
            return null;
        }
    }

    class InvalidateType extends AbstractRegistryAction {
        private final Iterable types;

        InvalidateType(Iterable types) {
            Preconditions.checkArgument(!Iterables.isEmpty(types), "No null or empty types list");
            this.types = types;
        }

        @Override
        Void doExecute() throws Exception {
            for (Type type : types) {
                registrationContainer.removeAll(type, defaultTimeOut, defaultTimeoutUnit);
            }
            return null;
        }
    }


    private class AddWatcher extends AbstractRegistryAction> {

        private final WatcherRegistration watcherRegistration;

        public AddWatcher(WatcherRegistration watcherRegistration) {
            this.watcherRegistration = watcherRegistration;
        }

        @Override
        Registration doExecute() throws Exception {
            registrationContainer.add(watcherRegistration, defaultTimeOut, defaultTimeoutUnit);
            return watcherRegistration;
        }
    }

    private class RemoveWatcher extends AbstractRegistryAction {
        private final Collection> watcherRegistrations;

        public RemoveWatcher(Collection> watcherRegistrations) {
            this.watcherRegistrations = watcherRegistrations;
        }

        @Override
        Void doExecute() throws Exception {
            for (WatcherRegistration watcherRegistration : watcherRegistrations) {
                registrationContainer.remove(watcherRegistration);
            }
            return null;
        }
    }

    private static class RegistryActionHandler implements Runnable {
        private static final Logger LOG = Logger.getLogger(RegistryActionHandler.class.getName());
        private final BlockingQueue registryActionQueue;

        RegistryActionHandler(BlockingQueue registryActionQueue) {
            this.registryActionQueue = registryActionQueue;
        }

        @Override
        public void run() {
            try {
                try {
                    for (; !Thread.currentThread().isInterrupted(); ) {
                        RegistryAction registryAction = registryActionQueue.take();
                        registryAction.execute();
                    }
                } catch (InterruptedException e) {
                    LOG.fine("Exit on interruption");
                }
                for (RegistryAction registryAction = registryActionQueue.poll(); registryAction != null; ) {
                    try {
                        registryAction.asFuture().cancel(true);
                    } catch (Exception ex) {
                        LOG.log(Level.SEVERE, "Error on cancel to exit on interruption", ex);
                    }
                }
            } catch (Exception e) {
                LOG.log(Level.SEVERE, "Exit on exception", e);
            }
        }
    }

    static SimpleRegistry newMultimapRegistry() {
        return new SimpleRegistry(newMultimapGuiceWatchableRegistrationContainer());
    }

    static SimpleRegistry newLoadingCacheRegistry() {
        return new SimpleRegistry(newLoadingCacheGuiceWatchableRegistrationContainer());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy