/* * 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.pubsub; import net.openhft.chronicle.core.Jvm; import net.openhft.chronicle.engine.api.pubsub.*; 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.engine.server.internal.TopicPublisherHandler.EventId; import net.openhft.chronicle.engine.server.internal.TopicPublisherHandler.Params; import net.openhft.chronicle.network.connection.AbstractAsyncSubscription; import net.openhft.chronicle.network.connection.AbstractStatelessClient; import net.openhft.chronicle.network.connection.CoreFields; import net.openhft.chronicle.network.connection.TcpChannelHub; import net.openhft.chronicle.wire.ValueIn; import net.openhft.chronicle.wire.WireIn; import net.openhft.chronicle.wire.WireOut; import net.openhft.chronicle.wire.Wires; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import static net.openhft.chronicle.engine.server.internal.PublisherHandler.EventId.registerSubscriber; import static net.openhft.chronicle.engine.server.internal.TopicPublisherHandler.EventId.onEndOfSubscription; import static net.openhft.chronicle.engine.server.internal.TopicPublisherHandler.EventId.publish; /** * Created by Rob Austin */ public class RemoteTopicPublisher<T, M> extends AbstractStatelessClient<EventId> implements TopicPublisher<T, M> { final Class<T> topicClass; final Class<M> messageClass; public RemoteTopicPublisher(@NotNull RequestContext context, @NotNull Asset asset) throws AssetNotFoundException { super(asset.findView(TcpChannelHub.class), (long) 0, toUri(context, "topicPublisher")); topicClass = context.topicType(); messageClass = context.messageType(); } protected RemoteTopicPublisher(@NotNull RequestContext context, @NotNull Asset asset, String view) throws AssetNotFoundException { super(asset.findView(TcpChannelHub.class), (long) 0, toUri(context, view)); topicClass = context.keyType(); messageClass = context.valueType(); } private static String toUri(@NotNull final RequestContext context, String view) { @NotNull final StringBuilder uri = new StringBuilder(context.fullName() + "?view=" + view); if (context.keyType() != String.class) uri.append("&keyType=").append(context.keyType().getName()); if (context.valueType() != String.class) uri.append("&valueType=").append(context.valueType().getName()); if (context.dontPersist()) uri.append("&dontPersist=").append(context.dontPersist()); return uri.toString(); } @Override public void publish(@NotNull final T topic, @NotNull final M message) { checkTopic(topic); checkMessage(message); sendEventAsync(publish, valueOut -> valueOut.marshallable(m -> { m.write(Params.topic).object(topic); m.write(Params.message).object(message); }), true); } private void checkTopic(@Nullable Object topic) { if (topic == null) throw new NullPointerException("topic can not be null"); } private void checkMessage(@Nullable Object message) { if (message == null) throw new NullPointerException("message can not be null"); } @Override public void registerTopicSubscriber(@NotNull final TopicSubscriber<T, M> topicSubscriber) throws AssetNotFoundException { if (hub.outBytesLock().isHeldByCurrentThread()) throw new IllegalStateException("Cannot view map while debugging"); hub.subscribe(new AbstractAsyncSubscription(hub, csp, "Remote Topic publisher register subscribe") { @Override public void onSubscribe(@NotNull final WireOut wireOut) { wireOut.writeEventName(registerSubscriber).text(""); } @Override public void onConsumer(@NotNull final WireIn w) { w.readDocument(null, d -> { final StringBuilder eventname = Wires.acquireStringBuilder(); @NotNull final ValueIn valueIn = d.readEventName(eventname); if (onEndOfSubscription.contentEquals(eventname)) { topicSubscriber.onEndOfSubscription(); hub.unsubscribe(tid()); } else if (CoreFields.reply.contentEquals(eventname)) { valueIn.marshallable(m -> { @Nullable final T topic = m.read(() -> "topic").object(topicClass); @Nullable final M message = m.read(() -> "message").object(messageClass); try { RemoteTopicPublisher.this.onEvent(topic, message, topicSubscriber); } catch (InvalidSubscriberException e) { throw Jvm.rethrow(e); } }); } }); } }); } @Override public void unregisterTopicSubscriber(@NotNull TopicSubscriber<T, M> topicSubscriber) { // TODO CE-101 throw new UnsupportedOperationException("todo"); } @NotNull @Override public Publisher<M> publisher(@NotNull T topic) { throw new UnsupportedOperationException("tood"); } @Override public void registerSubscriber(@NotNull T topic, @NotNull Subscriber<M> subscriber) { } private void onEvent(T topic, @Nullable M message, @NotNull TopicSubscriber<T, M> topicSubscriber) throws InvalidSubscriberException { if (message != null) { topicSubscriber.onMessage(topic, message); } else { // todo } } }