/* * -----------------------------------------------------------------------\ * PerfCake *   * Copyright (C) 2010 - 2016 the original author or authors. *   * 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 org.perfcake.message.sender; import org.perfcake.PerfCakeException; import org.perfcake.message.Message; import org.perfcake.reporting.MeasurementUnit; import org.perfcake.util.Utils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.Serializable; import java.net.Socket; import java.util.Properties; /** * The common ancestor for all senders that are able to send messages through a socket. * * @author <a href="mailto:marvenec@gmail.com">Martin Večeřa</a> */ public abstract class AbstractSocketSender extends AbstractSender { /** * The host of the socket. */ protected String host; /** * The port of the socket. */ protected int port; /** * The socket for sending. */ protected Socket socket; /** * A writer for message to be written into the socket. */ private PrintWriter out; /** * A reader for response to be received from the socket. */ private BufferedReader in; /** * The sender's logger. */ private final Logger log = LogManager.getLogger(AbstractSocketSender.class); @Override public void doInit(final Properties messageAttributes) throws PerfCakeException { final String[] parts = safeGetTarget(messageAttributes).split(":", 2); host = parts[0]; port = Integer.parseInt(parts[1]); } @Override public void doClose() { // closed per message } /** * Opens a socket on the {@link #host} address. * * @throws Exception * When it was not possible to open the socket. */ protected abstract void openSocket() throws Exception; /** * Opens the writer to an outbound socket's stream and the reader to read from inbound socket's stream. * * @throws Exception * When it was not possible to open socket streams. */ private void openStreams() throws Exception { out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), Utils.getDefaultEncoding()), true); in = new BufferedReader(new InputStreamReader(socket.getInputStream(), Utils.getDefaultEncoding())); } /** * Closes the socket along with the outbound and inbound streams. */ private void closeSocket() { out.close(); try { in.close(); } catch (final IOException e) { log.warn("Cannot close input stream.", e); } try { socket.close(); } catch (final IOException e) { log.warn("Cannot close socket.", e); } } @Override public void preSend(final Message message, final Properties messageAttributes) throws Exception { super.preSend(message, messageAttributes); openSocket(); openStreams(); } @Override public Serializable doSend(final Message message, final MeasurementUnit measurementUnit) throws Exception { out.print(message.getPayload().toString()); if (out.checkError()) { // flush and check for error throw new PerfCakeException(String.format("Error writing to a socket at %s:%d.", host, port)); } final StringBuilder sb = new StringBuilder(); while (in.ready()) { sb.append(in.readLine()); } return sb.toString(); } @Override public void postSend(final Message message) throws Exception { super.postSend(message); closeSocket(); } }