/* * Copyright 2014 WANdisco * * WANdisco licenses this file to you 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 c5db; import c5db.interfaces.C5Module; import c5db.interfaces.ModuleInformationProvider; import c5db.messages.generated.ModuleType; import c5db.util.FiberOnly; import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.Service; import com.google.common.util.concurrent.SettableFuture; import org.jetlang.channels.Channel; import org.jetlang.channels.MemoryChannel; import org.jetlang.channels.Subscriber; import org.jetlang.fibers.Fiber; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; /** * A basic ModuleInformationProvider that enables one to register and start C5Modules, * after which their life cycle is tracked using a SimpleC5ModuleListener. */ public class SimpleModuleInformationProvider implements ModuleInformationProvider { private final Fiber fiber; private final Consumer<Throwable> failureHandler; private final Map<ModuleType, C5Module> modules = new HashMap<>(); private final Map<ModuleType, Integer> onlineModuleToPortMap = new HashMap<>(); private final Channel<ImmutableMap<ModuleType, Integer>> moduleChangeChannel = new MemoryChannel<>(); public SimpleModuleInformationProvider(Fiber fiber, Consumer<Throwable> failureHandler) { this.fiber = fiber; this.failureHandler = failureHandler; } public ListenableFuture<Service.State> startModule(C5Module module) { SettableFuture<Service.State> startedFuture = SettableFuture.create(); Service.Listener stateChangeListener = new SimpleC5ModuleListener( module, () -> { addRunningModule(module); startedFuture.set(null); }, () -> removeModule(module), failureHandler); module.addListener(stateChangeListener, fiber); modules.put(module.getModuleType(), module); module.start(); return startedFuture; } @Override public ListenableFuture<C5Module> getModule(ModuleType moduleType) { SettableFuture<C5Module> moduleFuture = SettableFuture.create(); fiber.execute(() -> moduleFuture.set(modules.get(moduleType))); return moduleFuture; } @Override public ListenableFuture<ImmutableMap<ModuleType, Integer>> getOnlineModules() { final SettableFuture<ImmutableMap<ModuleType, Integer>> future = SettableFuture.create(); fiber.execute(() -> future.set(ImmutableMap.copyOf(onlineModuleToPortMap))); return future; } @Override public Subscriber<ImmutableMap<ModuleType, Integer>> moduleChangeChannel() { return moduleChangeChannel; } @Override public ImmutableMap<ModuleType, C5Module> getModules() throws ExecutionException, InterruptedException, TimeoutException { throw new UnsupportedOperationException(); } @FiberOnly private void addRunningModule(C5Module module) { ModuleType type = module.getModuleType(); if (modules.containsKey(type) && modules.get(type).equals(module)) { onlineModuleToPortMap.put(type, module.port()); publishCurrentActivePorts(); } } @FiberOnly private void removeModule(C5Module module) { ModuleType type = module.getModuleType(); if (modules.containsKey(type) && modules.get(type).equals(module)) { modules.remove(type); onlineModuleToPortMap.remove(type); publishCurrentActivePorts(); } } @FiberOnly private void publishCurrentActivePorts() { moduleChangeChannel.publish(ImmutableMap.copyOf(onlineModuleToPortMap)); } }