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 extends T> supplier) {
return put(id, requireNonNull(newSupplierAdapter(id, supplier), "supplier"));
}
private GuavaSupplierAdapter newSupplierAdapter(Id id, com.google.common.base.Supplier extends T> 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 extends T> supplier) {
requireNonNull(supplier, "supplier");
}
@Override
public void remove(org.javabits.yar.Registration> registration) {
removeAll(singletonList(registration));
}
@Override
public void removeAll(Collection extends Registration>> 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 extends Registration>> 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