/** * VMware Continuent Tungsten Replicator * Copyright (C) 2015 VMware, Inc. All rights reserved. * * 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. * * Initial developer(s): Robert Hodges * Contributor(s): */ package com.continuent.tungsten.common.sockets; import java.io.IOException; import java.net.InetSocketAddress; import javax.net.ssl.SSLSocket; import org.apache.log4j.Logger; import com.continuent.tungsten.common.config.cluster.ConfigurationException; import com.continuent.tungsten.common.security.SecurityHelper; /** * Implements a simple client that connects to server and sends a string to the * server at regular intervals. */ public class EchoClient implements Runnable { private static Logger logger = Logger.getLogger(EchoClient.class); // Client properties. private final String host; private final int port; private final boolean useSSL; private final long sleepMillis; // Operational variables. private ClientSocketWrapper socket; private volatile boolean shutdownRequested = false; private boolean isShutdown = false; private Throwable throwable; private Thread clientThread; private volatile String clientName; private int echoCount = 0; private String[] enabledCiphers; private String[] enabledProtocols; /** * Returns the enabledCiphers value. * * @return Returns the enabledCiphers. */ public String[] getEnabledCiphers() { return enabledCiphers; } /** * Sets the enabledCiphers value. * * @param enabledCiphers The enabledCiphers to set. */ public void setEnabledCiphers(String[] enabledCiphers) { this.enabledCiphers = enabledCiphers; } /** * Returns the enabledProtocols value. * * @return Returns the enabledProtocols. */ public String[] getEnabledProtocols() { return enabledProtocols; } /** * Sets the enabledProtocols value. * * @param enabledProtocols The enabledProtocols to set. */ public void setEnabledProtocols(String[] enabledProtocols) { this.enabledProtocols = enabledProtocols; } /** * Create a new echo server instance. */ public EchoClient(String host, int port, boolean useSSL, long sleepMillis) { this.host = host; this.port = port; this.useSSL = useSSL; this.sleepMillis = sleepMillis; } public Throwable getThrowable() { return throwable; } public int getEchoCount() { return echoCount; } public String getName() { return clientName; } /** * Starts the server. */ public synchronized void start() throws IOException, ConfigurationException { // Configure and connect. logger.info("Connecting client to server: host=" + host + " port=" + port + " useSSL=" + useSSL + " sleepMillis=" + sleepMillis); socket = new ClientSocketWrapper(); socket.setAddress(new InetSocketAddress(host, port)); socket.setUseSSL(useSSL); socket.connect(); // Spawn ourselves in a separate server. clientThread = new Thread(this); clientName = clientThread.getName(); clientThread.start(); logger.info("Spawned client thread: " + clientName); } /** * Loop through answering all incoming requests. */ @Override public void run() { try { doRun(); } catch (SocketTerminationException e) { logger.info("Client stopped by close on socket"); } catch (InterruptedException e) { logger.info("Client stopped by interrupt on thread"); } catch (Throwable t) { if (!this.shutdownRequested) { throwable = t; logger.info("Echo client failed: name=" + clientName + " throwable=" + throwable.getMessage(), t); } } finally { socket.close(); } } /** * Implements basic server processing, which continues until a call to * shutdown or the thread is interrupted. */ private void doRun() throws IOException, InterruptedException { SocketHelper helper = new SocketHelper(); while (shutdownRequested == false) { synchronized (this) { String echoValue = helper.echo(socket.getSocket(), clientName); if (!clientName.equals(echoValue)) throw new RuntimeException( "Echo returned unexpected value: client=" + clientName + " echoValue=" + echoValue); echoCount++; } Thread.sleep(sleepMillis); } } /** * Shut down a running client nicely, returning true if the thread is * finished. */ public synchronized boolean shutdown() { if (isShutdown) return !clientThread.isAlive(); logger.info("Shutting down echo client: " + clientName + " echoCount=" + echoCount); shutdownRequested = true; socket.close(); clientThread.interrupt(); try { clientThread.join(5000); } catch (InterruptedException e) { logger.warn("Unable to shut down echo client: " + clientName); } finally { isShutdown = true; } return !clientThread.isAlive(); } }