Java tutorial
package info.fetter.logstashforwarder.protocol; /* * Copyright 2015 Didier Fetter * * 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. * */ import info.fetter.logstashforwarder.Event; import info.fetter.logstashforwarder.ProtocolAdapter; import info.fetter.logstashforwarder.util.AdapterException; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ProtocolException; import java.net.Socket; import java.security.KeyStore; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.zip.Deflater; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManagerFactory; import org.apache.commons.io.HexDump; import org.apache.log4j.Logger; public class LumberjackClient implements ProtocolAdapter { private final static Logger logger = Logger.getLogger(LumberjackClient.class); private final static byte PROTOCOL_VERSION = 0x31; private final static byte FRAME_ACK = 0x41; private final static byte FRAME_WINDOW_SIZE = 0x57; private final static byte FRAME_DATA = 0x44; private final static byte FRAME_COMPRESSED = 0x43; private Socket socket; private SSLSocket sslSocket; private KeyStore keyStore; private String server; private int port; private DataOutputStream output; private DataInputStream input; private int sequence = 1; public LumberjackClient(String keyStorePath, String server, int port, int timeout) throws IOException { this.server = server; this.port = port; try { if (keyStorePath == null) { throw new IOException("Key store not configured"); } if (server == null) { throw new IOException("Server address not configured"); } keyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream(keyStorePath), null); TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); tmf.init(keyStore); SSLContext context = SSLContext.getInstance("TLS"); context.init(null, tmf.getTrustManagers(), null); SSLSocketFactory socketFactory = context.getSocketFactory(); socket = new Socket(); socket.connect(new InetSocketAddress(InetAddress.getByName(server), port), timeout); socket.setSoTimeout(timeout); sslSocket = (SSLSocket) socketFactory.createSocket(socket, server, port, true); sslSocket.setUseClientMode(true); sslSocket.startHandshake(); output = new DataOutputStream(new BufferedOutputStream(sslSocket.getOutputStream())); input = new DataInputStream(sslSocket.getInputStream()); logger.info("Connected to " + server + ":" + port); } catch (IOException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } public int sendWindowSizeFrame(int size) throws IOException { output.writeByte(PROTOCOL_VERSION); output.writeByte(FRAME_WINDOW_SIZE); output.writeInt(size); output.flush(); if (logger.isDebugEnabled()) { logger.debug("Sending window size frame : " + size + " frames"); } return 6; } private int sendDataFrame(DataOutputStream output, Map<String, byte[]> keyValues) throws IOException { output.writeByte(PROTOCOL_VERSION); output.writeByte(FRAME_DATA); output.writeInt(sequence++); output.writeInt(keyValues.size()); int bytesSent = 10; for (String key : keyValues.keySet()) { int keyLength = key.length(); output.writeInt(keyLength); bytesSent += 4; output.write(key.getBytes()); bytesSent += keyLength; byte[] value = keyValues.get(key); output.writeInt(value.length); bytesSent += 4; output.write(value); bytesSent += value.length; } output.flush(); return bytesSent; } public int sendDataFrameInSocket(Map<String, byte[]> keyValues) throws IOException { return sendDataFrame(output, keyValues); } public int sendCompressedFrame(List<Map<String, byte[]>> keyValuesList) throws IOException { output.writeByte(PROTOCOL_VERSION); output.writeByte(FRAME_COMPRESSED); ByteArrayOutputStream uncompressedBytes = new ByteArrayOutputStream(); DataOutputStream uncompressedOutput = new DataOutputStream(uncompressedBytes); for (Map<String, byte[]> keyValues : keyValuesList) { logger.trace("Adding data frame"); sendDataFrame(uncompressedOutput, keyValues); } uncompressedOutput.close(); Deflater compressor = new Deflater(); byte[] uncompressedData = uncompressedBytes.toByteArray(); if (logger.isDebugEnabled()) { logger.debug("Deflating data : " + uncompressedData.length + " bytes"); } if (logger.isTraceEnabled()) { HexDump.dump(uncompressedData, 0, System.out, 0); } compressor.setInput(uncompressedData); compressor.finish(); ByteArrayOutputStream compressedBytes = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; while (!compressor.finished()) { int count = compressor.deflate(buffer); compressedBytes.write(buffer, 0, count); } compressedBytes.close(); byte[] compressedData = compressedBytes.toByteArray(); if (logger.isDebugEnabled()) { logger.debug("Deflated data : " + compressor.getTotalOut() + " bytes"); } if (logger.isTraceEnabled()) { HexDump.dump(compressedData, 0, System.out, 0); } output.writeInt(compressor.getTotalOut()); output.write(compressedData); output.flush(); if (logger.isDebugEnabled()) { logger.debug("Sending compressed frame : " + keyValuesList.size() + " frames"); } return 6 + compressor.getTotalOut(); } public int readAckFrame() throws ProtocolException, IOException { byte protocolVersion = input.readByte(); if (protocolVersion != PROTOCOL_VERSION) { throw new ProtocolException("Protocol version should be 1, received " + protocolVersion); } byte frameType = input.readByte(); if (frameType != FRAME_ACK) { throw new ProtocolException("Frame type should be Ack, received " + frameType); } int sequenceNumber = input.readInt(); if (logger.isDebugEnabled()) { logger.debug("Received ack sequence : " + sequenceNumber); } return sequenceNumber; } public int sendEvents(List<Event> eventList) throws AdapterException { try { int beginSequence = sequence; int numberOfEvents = eventList.size(); if (logger.isInfoEnabled()) { logger.info("Sending " + numberOfEvents + " events"); } sendWindowSizeFrame(numberOfEvents); List<Map<String, byte[]>> keyValuesList = new ArrayList<Map<String, byte[]>>(numberOfEvents); for (Event event : eventList) { keyValuesList.add(event.getKeyValues()); } sendCompressedFrame(keyValuesList); while (readAckFrame() < (sequence - 1)) { } return sequence - beginSequence; } catch (Exception e) { throw new AdapterException(e); } } public void close() throws AdapterException { try { sslSocket.close(); } catch (Exception e) { throw new AdapterException(e); } logger.info("Connection to " + server + ":" + port + " closed"); } public String getServer() { return server; } public int getPort() { return port; } }