/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF 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 org.apache.ignite.stream.socket; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.ByteOrder; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteDataStreamer; import org.apache.ignite.IgniteException; import org.apache.ignite.IgniteLogger; import org.apache.ignite.internal.util.nio.GridBufferedParser; import org.apache.ignite.internal.util.nio.GridDelimitedParser; import org.apache.ignite.internal.util.nio.GridNioCodecFilter; import org.apache.ignite.internal.util.nio.GridNioFilter; import org.apache.ignite.internal.util.nio.GridNioParser; import org.apache.ignite.internal.util.nio.GridNioServer; import org.apache.ignite.internal.util.nio.GridNioServerListener; import org.apache.ignite.internal.util.nio.GridNioServerListenerAdapter; import org.apache.ignite.internal.util.nio.GridNioSession; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.A; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.marshaller.Marshaller; import org.apache.ignite.marshaller.MarshallerUtils; import org.apache.ignite.stream.StreamAdapter; import org.apache.ignite.stream.StreamTupleExtractor; import org.jetbrains.annotations.Nullable; /** * Server that receives data from TCP socket, converts it to key-value pairs using {@link StreamTupleExtractor} and * streams into {@link IgniteDataStreamer} instance. * <p> * By default server uses size-based message processing. That is every message sent over the socket is prepended with * 4-byte integer header containing message size. If message delimiter is defined (see {@link #setDelimiter}) then * delimiter-based message processing will be used. That is every message sent over the socket is appended with * provided delimiter. * <p> * Received messages through socket converts to Java object using standard serialization. Conversion functionality * can be customized via user defined {@link SocketMessageConverter} (e.g. in order to convert messages from * non Java clients). */ public class SocketStreamer<T, K, V> extends StreamAdapter<T, K, V> { /** Default threads. */ private static final int DFLT_THREADS = Runtime.getRuntime().availableProcessors(); /** Logger. */ private IgniteLogger log; /** Address. */ private InetAddress addr; /** Server port. */ private int port; /** Threads number. */ private int threads = DFLT_THREADS; /** Direct mode. */ private boolean directMode; /** Delimiter. */ private byte[] delim; /** Converter. */ private SocketMessageConverter<T> converter; /** Server. */ private GridNioServer<byte[]> srv; /** * Sets server address. * * @param addr Address. */ public void setAddr(InetAddress addr) { this.addr = addr; } /** * Sets port number. * * @param port Port. */ public void setPort(int port) { this.port = port; } /** * Sets threadds amount. * * @param threads Threads. */ public void setThreads(int threads) { this.threads = threads; } /** * Sets direct mode flag. * * @param directMode Direct mode. */ public void setDirectMode(boolean directMode) { this.directMode = directMode; } /** * Sets message delimiter. * * @param delim Delimiter. */ public void setDelimiter(byte[] delim) { this.delim = delim; } /** * Sets message converter. * * @param converter Converter. */ public void setConverter(SocketMessageConverter<T> converter) { this.converter = converter; } /** * Starts streamer. * * @throws IgniteException If failed. */ public void start() { A.ensure(getSingleTupleExtractor() != null || getMultipleTupleExtractor() != null, "tupleExtractor (single or multiple)"); A.notNull(getStreamer(), "streamer"); A.notNull(getIgnite(), "ignite"); A.ensure(threads > 0, "threads > 0"); log = getIgnite().log(); GridNioServerListener<byte[]> lsnr = new GridNioServerListenerAdapter<byte[]>() { @Override public void onConnected(GridNioSession ses) { assert ses.accepted(); if (log.isDebugEnabled()) log.debug("Accepted connection: " + ses.remoteAddress()); } @Override public void onDisconnected(GridNioSession ses, @Nullable Exception e) { if (e != null) log.error("Connection failed with exception", e); } @Override public void onMessage(GridNioSession ses, byte[] msg) { addMessage(converter.convert(msg)); } }; ByteOrder byteOrder = ByteOrder.BIG_ENDIAN; GridNioParser parser = F.isEmpty(delim) ? new GridBufferedParser(directMode, byteOrder) : new GridDelimitedParser(delim, directMode); if (converter == null) converter = new DefaultConverter<>(getIgnite().name()); GridNioFilter codec = new GridNioCodecFilter(parser, log, directMode); GridNioFilter[] filters = new GridNioFilter[] {codec}; try { srv = new GridNioServer.Builder<byte[]>() .address(addr == null ? InetAddress.getLocalHost() : addr) .serverName("sock-streamer") .port(port) .listener(lsnr) .logger(log) .selectorCount(threads) .byteOrder(byteOrder) .filters(filters) .build(); } catch (IgniteCheckedException | UnknownHostException e) { throw new IgniteException(e); } srv.start(); if (log.isDebugEnabled()) log.debug("Socket streaming server started on " + addr + ':' + port); } /** * Stops streamer. */ public void stop() { if (srv != null) srv.stop(); if (log.isDebugEnabled()) log.debug("Socket streaming server stopped"); } /** * Converts message to Java object using Jdk marshaller. */ private static class DefaultConverter<T> implements SocketMessageConverter<T> { /** Marshaller. */ private final Marshaller marsh; /** * Constructor. * * @param igniteInstanceName Ignite instance name. */ private DefaultConverter(@Nullable String igniteInstanceName) { marsh = MarshallerUtils.jdkMarshaller(igniteInstanceName); } /** {@inheritDoc} */ @Override public T convert(byte[] msg) { try { return U.unmarshal(marsh, msg, null); } catch (IgniteCheckedException e) { throw new IgniteException(e); } } } }