001/* 002 * VM-Operator 003 * Copyright (C) 2023 Michael N. Lipp 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <https://www.gnu.org/licenses/>. 017 */ 018 019package org.jdrupes.vmoperator.manager.events; 020 021import java.util.Collection; 022import java.util.Map; 023import java.util.Optional; 024import java.util.Set; 025import java.util.concurrent.ConcurrentHashMap; 026import java.util.function.Function; 027import org.jgrapes.core.Channel; 028 029/** 030 * A channel manager that maintains mappings from a key to a channel. 031 * As a convenience, it is possible to additionally associate arbitrary 032 * data with the entry (and thus with the channel). 033 * 034 * The manager should be used by a component that defines channels for 035 * housekeeping. It can be shared between this component and another 036 * component, preferably using the {@link #fixed()} view for the 037 * second component. Alternatively, the second component can use a 038 * {@link ChannelCache} to track the mappings using events. 039 * 040 * @param <K> the key type 041 * @param <C> the channel type 042 * @param <A> the type of the associated data 043 */ 044public class ChannelManager<K, C extends Channel, A> { 045 046 private final Map<K, Both<C, A>> channels = new ConcurrentHashMap<>(); 047 private final Function<K, C> supplier; 048 private ChannelManager<K, C, A> readOnly; 049 050 /** 051 * Combines the channel and the associated data. 052 * 053 * @param <C> the generic type 054 * @param <A> the generic type 055 */ 056 @SuppressWarnings("PMD.ShortClassName") 057 public static class Both<C extends Channel, A> { 058 059 /** The channel. */ 060 public C channel; 061 062 /** The associated. */ 063 public A associated; 064 065 /** 066 * Instantiates a new both. 067 * 068 * @param channel the channel 069 * @param associated the associated 070 */ 071 public Both(C channel, A associated) { 072 super(); 073 this.channel = channel; 074 this.associated = associated; 075 } 076 } 077 078 /** 079 * Instantiates a new channel manager. 080 * 081 * @param supplier the supplier that creates new channels 082 */ 083 public ChannelManager(Function<K, C> supplier) { 084 this.supplier = supplier; 085 } 086 087 /** 088 * Instantiates a new channel manager without a default supplier. 089 */ 090 public ChannelManager() { 091 this(k -> null); 092 } 093 094 /** 095 * Returns the channel and associates data registered for the key 096 * or an empty optional if no mapping exists. 097 * 098 * @param key the key 099 * @return the result 100 */ 101 public Optional<Both<C, A>> both(K key) { 102 synchronized (channels) { 103 return Optional.ofNullable(channels.get(key)); 104 } 105 } 106 107 /** 108 * Store the given data. 109 * 110 * @param key the key 111 * @param channel the channel 112 * @param associated the associated 113 * @return the channel manager 114 */ 115 public ChannelManager<K, C, A> put(K key, C channel, A associated) { 116 channels.put(key, new Both<>(channel, associated)); 117 return this; 118 } 119 120 /** 121 * Store the given data. 122 * 123 * @param key the key 124 * @param channel the channel 125 * @return the channel manager 126 */ 127 public ChannelManager<K, C, A> put(K key, C channel) { 128 put(key, channel, null); 129 return this; 130 } 131 132 /** 133 * Returns the channel registered for the key or an empty optional 134 * if no mapping exists. 135 * 136 * @param key the key 137 * @return the optional 138 */ 139 public Optional<C> channel(K key) { 140 return both(key).map(b -> b.channel); 141 } 142 143 /** 144 * Returns the {@link Channel} for the given name, creating it using 145 * the supplier passed to the constructor if it doesn't exist yet. 146 * 147 * @param key the key 148 * @return the channel 149 */ 150 public Optional<C> getChannel(K key) { 151 return getChannel(key, supplier); 152 } 153 154 /** 155 * Returns the {@link Channel} for the given name, creating it using 156 * the given supplier if it doesn't exist yet. 157 * 158 * @param key the key 159 * @param supplier the supplier 160 * @return the channel 161 */ 162 @SuppressWarnings({ "PMD.AssignmentInOperand", 163 "PMD.DataflowAnomalyAnalysis" }) 164 public Optional<C> getChannel(K key, Function<K, C> supplier) { 165 synchronized (channels) { 166 return Optional 167 .of(Optional.ofNullable(channels.get(key)) 168 .map(v -> v.channel) 169 .orElseGet(() -> { 170 var channel = supplier.apply(key); 171 channels.put(key, new Both<>(channel, null)); 172 return channel; 173 })); 174 } 175 } 176 177 /** 178 * Associate the entry for the channel with the given data. The entry 179 * for the channel must already exist. 180 * 181 * @param key the key 182 * @param data the data 183 * @return the channel manager 184 */ 185 public ChannelManager<K, C, A> associate(K key, A data) { 186 synchronized (channels) { 187 Optional.ofNullable(channels.get(key)) 188 .ifPresent(v -> v.associated = data); 189 } 190 return this; 191 } 192 193 /** 194 * Return the data associated with the entry for the channel. 195 * 196 * @param key the key 197 * @return the data 198 */ 199 public Optional<A> associated(K key) { 200 return both(key).map(b -> b.associated); 201 } 202 203 /** 204 * Returns all associated data. 205 * 206 * @return the collection 207 */ 208 public Collection<A> associated() { 209 synchronized (channels) { 210 return channels.values().stream() 211 .filter(v -> v.associated != null) 212 .map(v -> v.associated).toList(); 213 } 214 } 215 216 /** 217 * Removes the channel with the given name. 218 * 219 * @param name the name 220 */ 221 public void remove(String name) { 222 synchronized (channels) { 223 channels.remove(name); 224 } 225 } 226 227 /** 228 * Returns all known keys. 229 * 230 * @return the sets the 231 */ 232 public Set<K> keys() { 233 return channels.keySet(); 234 } 235 236 /** 237 * Returns a read only view of this channel manager. The methods 238 * that usually create a new entry refrain from doing so. The 239 * methods that change the value of channel and {@link #remove(String)} 240 * do nothing. The associated data, however, can still be changed. 241 * 242 * @return the channel manager 243 */ 244 public ChannelManager<K, C, A> fixed() { 245 if (readOnly == null) { 246 readOnly = new ChannelManager<>(supplier) { 247 248 @Override 249 public Optional<Both<C, A>> both(K key) { 250 return ChannelManager.this.both(key); 251 } 252 253 @Override 254 public ChannelManager<K, C, A> put(K key, C channel, 255 A associated) { 256 return associate(key, associated); 257 } 258 259 @Override 260 public Optional<C> getChannel(K key) { 261 return ChannelManager.this.channel(key); 262 } 263 264 @Override 265 public Optional<C> getChannel(K key, Function<K, C> supplier) { 266 return ChannelManager.this.channel(key); 267 } 268 269 @Override 270 public ChannelManager<K, C, A> associate(K key, A data) { 271 return ChannelManager.this.associate(key, data); 272 } 273 274 @Override 275 public Optional<A> associated(K key) { 276 return ChannelManager.this.associated(key); 277 } 278 279 @Override 280 public Collection<A> associated() { 281 return ChannelManager.this.associated(); 282 } 283 284 @Override 285 public void remove(String name) { 286 // Do nothing 287 } 288 289 @Override 290 public Set<K> keys() { 291 return ChannelManager.this.keys(); 292 } 293 294 @Override 295 public ChannelManager<K, C, A> fixed() { 296 return ChannelManager.this.fixed(); 297 } 298 299 }; 300 } 301 return readOnly; 302 } 303}