/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with VoltDB. If not, see <http://www.gnu.org/licenses/>. */ package org.voltdb.importclient.socket; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; import java.net.URI; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import org.voltcore.logging.Level; import org.voltdb.importer.AbstractImporter; import org.voltdb.importer.Invocation; import org.voltdb.importer.formatter.FormatException; import org.voltdb.importer.formatter.Formatter; /** * Importer that listens on a server socket for data. Data is expected in CSV format currently, * which will be parsed and sent to the procedure specified in the configuration. */ public class ServerSocketImporter extends AbstractImporter { private final ServerSocketImporterConfig m_config; private List<ClientConnectionHandler> m_clients = new ArrayList<>(); public ServerSocketImporter(ServerSocketImporterConfig config) { m_config = config; } @Override public String getName() { return "SocketServerImporter"; } @Override public URI getResourceID() { return m_config.getResourceID(); } @Override protected void accept() { startListening(); } @Override protected void stop() { try { m_config.getServerSocket().close(); } catch(IOException e) { warn(e, "Error closing socket importer server socket on port " + m_config.getPort()); } for (ClientConnectionHandler client : m_clients) { client.stopClient(); } } private void startListening() { try { while (shouldRun()) { Socket clientSocket = m_config.getServerSocket().accept(); ClientConnectionHandler ch = new ClientConnectionHandler(clientSocket, m_config.getProcedure()); m_clients.add(ch); ch.start(); } } catch(IOException e) { warn(e, "Unexpected error accepting client connections for " + getName() + " on port " + m_config.getPort()); } } //This is ClientConnection handler to read and dispatch data to stored procedure. private class ClientConnectionHandler extends Thread { private final Socket m_clientSocket; private final String m_procedure; public ClientConnectionHandler(Socket clientSocket, String procedure) { m_clientSocket = clientSocket; m_procedure = procedure; } @Override public void run() { try { BufferedReader in = new BufferedReader( new InputStreamReader(m_clientSocket.getInputStream())); Formatter formatter = m_config.getFormatterBuilder().create(); Object params[] = null; while (shouldRun()) { String line = in.readLine(); try{ params = formatter.transform(ByteBuffer.wrap(line.getBytes())); //You should convert your data to params here. if (params == null) continue; Invocation invocation = new Invocation(m_procedure, params); if (!callProcedure(invocation)) { rateLimitedLog(Level.ERROR, null, "Socket importer insertion failed"); } } catch (FormatException e){ rateLimitedLog(Level.ERROR, e, "Failed to tranform data: %s" ,line); } } } catch (IOException ioe) { error(ioe, "IO exception reading from client socket connection in socket importer"); } try { m_clientSocket.close(); info(null, "Client Closed."); } catch(IOException e) { warn(e, "Error closing socket importer connection"); } } public void stopClient() { // nothing to do for now } } }