/* * Copyright (C) 2015 SoftIndex LLC. * * 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 io.datakernel.datagraph.server; import io.datakernel.async.CompletionCallback; import io.datakernel.async.ConnectCallback; import io.datakernel.bytebuf.ByteBuf; import io.datakernel.datagraph.graph.StreamId; import io.datakernel.datagraph.node.Node; import io.datakernel.datagraph.server.command.DatagraphCommand; import io.datakernel.datagraph.server.command.DatagraphCommandDownload; import io.datakernel.datagraph.server.command.DatagraphCommandExecute; import io.datakernel.datagraph.server.command.DatagraphResponse; import io.datakernel.eventloop.AsyncTcpSocketImpl; import io.datakernel.eventloop.Eventloop; import io.datakernel.net.SocketSettings; import io.datakernel.serializer.BufferSerializer; import io.datakernel.stream.StreamConsumer; import io.datakernel.stream.StreamProducer; import io.datakernel.stream.net.MessagingSerializer; import io.datakernel.stream.net.MessagingWithBinaryStreaming; import io.datakernel.stream.processor.StreamBinaryDeserializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.InetSocketAddress; import java.nio.channels.SocketChannel; import java.util.ArrayList; import java.util.Collection; import java.util.List; import static io.datakernel.stream.net.MessagingSerializers.ofGson; /** * Client for datagraph server. * Sends JSON commands for performing certain actions on server. */ public final class DatagraphClient { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final Eventloop eventloop; private final DatagraphSerialization serialization; private final MessagingSerializer<DatagraphResponse, DatagraphCommand> serializer; private SocketSettings socketSettings = SocketSettings.create(); /** * Constructs a datagraph client that runs in a given event loop and uses the specified DatagraphSerialization object for various serialization purposes. * * @param eventloop event loop, in which client is to run * @param serialization DatagraphSerialization object used for serialization */ public DatagraphClient(Eventloop eventloop, DatagraphSerialization serialization) { this.eventloop = eventloop; this.serialization = serialization; this.serializer = ofGson(serialization.gson, DatagraphResponse.class, serialization.gson, DatagraphCommand.class); } public void connectAndExecute(InetSocketAddress address, ConnectCallback callback) { eventloop.connect(address, callback); } private class DownloadConnectCallback extends ConnectCallback { private final StreamId streamId; private final StreamConsumer<ByteBuf> consumer; private final CompletionCallback callback; public DownloadConnectCallback(StreamId streamId, StreamConsumer<ByteBuf> consumer, CompletionCallback callback) { this.streamId = streamId; this.consumer = consumer; this.callback = callback; } @Override public void onConnect(SocketChannel socketChannel) { AsyncTcpSocketImpl asyncTcpSocket = AsyncTcpSocketImpl.wrapChannel(eventloop, socketChannel, socketSettings); final MessagingWithBinaryStreaming<DatagraphResponse, DatagraphCommand> messaging = MessagingWithBinaryStreaming.create(eventloop, asyncTcpSocket, serializer); DatagraphCommandDownload commandDownload = new DatagraphCommandDownload(streamId); messaging.send(commandDownload, new CompletionCallback() { @Override public void onComplete() { messaging.receiveBinaryStreamTo(consumer, new CompletionCallback() { @Override public void onComplete() { messaging.close(); callback.setComplete(); } @Override public void onException(Exception e) { messaging.close(); callback.setException(e); } }); } @Override public void onException(Exception e) { messaging.close(); callback.setException(e); } }); asyncTcpSocket.setEventHandler(messaging); asyncTcpSocket.register(); } @Override public void onException(Exception e) { callback.setException(e); } } private class ExecuteConnectCallback extends ConnectCallback { private final List<Node> nodes; private final CompletionCallback callback; private ExecuteConnectCallback(List<Node> nodes, CompletionCallback callback) { this.nodes = nodes; this.callback = callback; } @Override public void onConnect(SocketChannel socketChannel) { AsyncTcpSocketImpl asyncTcpSocket = AsyncTcpSocketImpl.wrapChannel(eventloop, socketChannel, socketSettings); final MessagingWithBinaryStreaming<DatagraphResponse, DatagraphCommand> messaging = MessagingWithBinaryStreaming.create(eventloop, asyncTcpSocket, serializer); DatagraphCommandExecute commandExecute = new DatagraphCommandExecute(nodes); messaging.send(commandExecute, new CompletionCallback() { @Override public void onComplete() { messaging.close(); } @Override public void onException(Exception e) { messaging.close(); callback.setException(e); } }); asyncTcpSocket.setEventHandler(messaging); asyncTcpSocket.register(); } @Override public void onException(Exception e) { callback.setException(e); } } public <T> StreamProducer<T> download(InetSocketAddress address, final StreamId streamId, Class<T> type) { BufferSerializer<T> serializer = serialization.getSerializer(type); StreamBinaryDeserializer<T> deserializer = StreamBinaryDeserializer.create(eventloop, serializer); connectAndExecute(address, new DownloadConnectCallback(streamId, deserializer.getInput(), new CompletionCallback() { @Override public void onComplete() { logger.info("Downloading stream {} completed", streamId); } @Override public void onException(Exception e) { logger.error("Failed to download stream {}", streamId, e); } })); return deserializer.getOutput(); } public void execute(InetSocketAddress address, final Collection<Node> nodes) { connectAndExecute(address, new ExecuteConnectCallback(new ArrayList<>(nodes), new CompletionCallback() { @Override public void onComplete() { logger.info("Execute command sent to nodes {}", nodes); } @Override public void onException(Exception e) { logger.error("Failed to send execute command to nodes {}", nodes, e); } })); } }