/* * Copyright 2016 higherfrequencytrading.com * * 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 net.openhft.chronicle.engine.map; import net.openhft.chronicle.bytes.Bytes; import net.openhft.chronicle.bytes.BytesStore; import net.openhft.chronicle.core.util.ObjectUtils; import net.openhft.chronicle.engine.api.EngineReplication.ReplicationEntry; import net.openhft.chronicle.engine.api.map.*; import net.openhft.chronicle.engine.api.pubsub.InvalidSubscriberException; import net.openhft.chronicle.engine.api.pubsub.SubscriptionConsumer; import net.openhft.chronicle.engine.api.tree.Asset; import net.openhft.chronicle.engine.api.tree.AssetNotFoundException; import net.openhft.chronicle.engine.api.tree.RequestContext; import net.openhft.chronicle.wire.Marshallable; import net.openhft.chronicle.wire.Wire; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.function.BiFunction; import java.util.function.Function; import static net.openhft.chronicle.engine.map.Buffers.BUFFERS; /** * Created by peter on 25/05/15. */ public class VanillaStringMarshallableKeyValueStore<V extends Marshallable> implements StringMarshallableKeyValueStore<V> { @NotNull private final BiFunction<V, Bytes, Bytes> valueToBytes; @NotNull private final BiFunction<BytesStore, V, V> bytesToValue; @NotNull private final ObjectSubscription<String, V> subscriptions; @NotNull private final SubscriptionKeyValueStore<String, BytesStore> kvStore; @NotNull private final Asset asset; @NotNull private final Class<V> valueType; public VanillaStringMarshallableKeyValueStore(@NotNull RequestContext context, @NotNull Asset asset, @NotNull SubscriptionKeyValueStore<String, BytesStore> kvStore) throws AssetNotFoundException { this(asset.acquireView(ObjectSubscription.class, context), asset, context.valueType(), kvStore, context.wireType()); } VanillaStringMarshallableKeyValueStore(@NotNull ObjectSubscription<String, V> subscriptions, @NotNull Asset asset, @NotNull Class valueType, @NotNull SubscriptionKeyValueStore<String, BytesStore> kvStore, @NotNull Function<Bytes, Wire> wireType) { this.asset = asset; this.valueType = valueType; valueToBytes = toBytes(valueType, wireType); bytesToValue = fromBytes(valueType, wireType); this.kvStore = kvStore; @NotNull ValueReader<BytesStore, V> valueReader = bs -> bytesToValue.apply(bs, null); asset.registerView(ValueReader.class, valueReader); @NotNull RawKVSSubscription<String, BytesStore> rawSubscription = (RawKVSSubscription<String, BytesStore>) kvStore.subscription(true); this.subscriptions = subscriptions; rawSubscription.registerDownstream(mpe -> subscriptions.notifyEvent(mpe.translate(s -> s, b -> bytesToValue.apply(b, null)))); } static <T> BiFunction<T, Bytes, Bytes> toBytes(@NotNull Class type, @NotNull Function<Bytes, Wire> wireType) { if (type == String.class) return (t, bytes) -> (Bytes) bytes.appendUtf8((String) t); if (Marshallable.class.isAssignableFrom(type)) return (t, bytes) -> { t = acquireInstance(type, t); ((Marshallable) t).writeMarshallable(wireType.apply(bytes)); return bytes; }; throw new UnsupportedOperationException("todo"); } @Nullable static <T> T acquireInstance(@NotNull Class type, @Nullable T t) { if (t == null) t = (T) ObjectUtils.newInstance(type); return t; } private <T> BiFunction<BytesStore, T, T> fromBytes(@NotNull Class type, @NotNull Function<Bytes, Wire> wireType) { if (type == String.class) return (t, bytes) -> (T) (bytes == null ? null : bytes.toString()); if (Marshallable.class.isAssignableFrom(type)) return (bytes, t) -> { if (bytes == null) return null; t = acquireInstance(type, t); ((Marshallable) t).readMarshallable(wireType.apply(bytes.bytesForRead())); return t; }; throw new UnsupportedOperationException("todo"); } @NotNull @Override public ObjectSubscription<String, V> subscription(boolean createIfAbsent) { return subscriptions; } @Override public boolean put(String key, V value) { Buffers b = BUFFERS.get(); Bytes valueBytes = valueToBytes.apply(value, b.valueBuffer); return kvStore.put(key, valueBytes); } @Override public V getAndPut(String key, V value) { Buffers b = BUFFERS.get(); Bytes valueBytes = valueToBytes.apply(value, b.valueBuffer); @Nullable BytesStore retBytes = kvStore.getAndPut(key, valueBytes); return retBytes == null ? null : bytesToValue.apply(retBytes, null); } @Override public boolean remove(String key) { return kvStore.remove(key); } @Override public V getAndRemove(String key) { @Nullable BytesStore retBytes = kvStore.getAndRemove(key); return retBytes == null ? null : bytesToValue.apply(retBytes, null); } @Override public V getUsing(String key, Object value) { Buffers b = BUFFERS.get(); @Nullable BytesStore retBytes = kvStore.getUsing(key, b.valueBuffer); return retBytes == null ? null : bytesToValue.apply(retBytes, (V) value); } @Override public long longSize() { return kvStore.longSize(); } @Override public void keysFor(int segment, SubscriptionConsumer<String> kConsumer) throws InvalidSubscriberException { kvStore.keysFor(segment, kConsumer); } @Override public void entriesFor(int segment, @NotNull SubscriptionConsumer<MapEvent<String, V>> kvConsumer) throws InvalidSubscriberException { kvStore.entriesFor(segment, e -> kvConsumer.accept( InsertedEvent.of(asset.fullName(), e.getKey(), bytesToValue.apply(e.getValue(), null), false))); } @NotNull @Override public Iterator<Map.Entry<String, V>> entrySetIterator() { // todo optimise @NotNull List<Map.Entry<String, V>> entries = new ArrayList<>(); try { for (int i = 0, seg = segments(); i < seg; i++) entriesFor(i, e -> entries.add(new SimpleEntry<>(e.getKey(), e.getValue()))); } catch (InvalidSubscriberException e) { throw new AssertionError(e); } return entries.iterator(); } @NotNull @Override public Iterator<String> keySetIterator() { // todo optimise @NotNull List<String> entries = new ArrayList<>(); try { for (int i = 0, seg = segments(); i < seg; i++) keysFor(i, entries::add); } catch (InvalidSubscriberException e) { throw new AssertionError(e); } return entries.iterator(); } @Override public void clear() { kvStore.clear(); } @Override public boolean containsValue(final V value) { throw new UnsupportedOperationException("todo"); } @NotNull @Override public Asset asset() { return asset; } @Override public KeyValueStore underlying() { return kvStore; } @Override public void close() { kvStore.close(); } @NotNull @Override public Class<String> keyType() { return String.class; } @NotNull @Override public Class<V> valueType() { return valueType; } @Override public void accept(final ReplicationEntry replicationEntry) { throw new UnsupportedOperationException("todo"); } }