All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cp.elements.tools.net.ConnectionTester Maven / Gradle / Ivy

/*
 * Copyright 2011-Present 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.cp.elements.tools.net;

import static org.cp.elements.lang.LangExtensions.assertThat;
import static org.cp.elements.lang.NumberUtils.intValue;
import static org.cp.elements.lang.RuntimeExceptionsFactory.newIllegalArgumentException;
import static org.cp.elements.net.NetworkUtils.lenientParsePort;
import static org.cp.elements.net.NetworkUtils.newSocketAddress;

import java.io.IOException;
import java.net.Socket;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import org.cp.elements.lang.Condition;
import org.cp.elements.lang.StringUtils;
import org.cp.elements.net.ServicePort;
import org.cp.elements.test.Tester;

/**
 * The {@link ConnectionTester} class is used to test a network connection to a given network endpoint.
 *
 * @author John Blum
 * @see java.net.Socket
 * @see java.util.Optional
 * @see org.cp.elements.lang.Condition
 * @see org.cp.elements.net.NetworkUtils
 * @see org.cp.elements.net.ServicePort
 * @see org.cp.elements.test.Tester
 * @since 1.0.0
 */
@SuppressWarnings("unused")
public class ConnectionTester implements Condition, Tester {

  protected static final boolean SO_REUSEADDR = true;

  protected static final long CONNECT_TIMEOUT = TimeUnit.SECONDS.toMillis(1L);

  protected static final String HOST_PORT_SEPARATOR = ":";

  public static void main(String[] args) {

    Arrays.stream(args).forEach(hostPort -> {

      String[] hostPortSplit = hostPort.split(HOST_PORT_SEPARATOR);
      String host = (hostPortSplit.length > 1 ? hostPortSplit[0] : null);

      int port = lenientParsePort(hostPortSplit.length > 1 ? hostPortSplit[1] : hostPortSplit[0]);

      System.out.printf("Connection to [%1$s:%2$d] was %3$ssuccessful%n",
        Optional.ofNullable(host).orElse("localhost"), port,
          newConnectionTester(host, port).test() ? StringUtils.EMPTY_STRING : "not ");
    });
  }

  /**
   * Factory method used to construct a new instance of {@link ConnectionTester} in order to test a connection
   * to {@literal localhost} on the specified {@link Integer port}.
   *
   * @param port {@link Integer} value specifying the port number to which the connection is bound.
   * @return a new instance of {@link ConnectionTester} bound to the specified {@link Integer port}.
   * @throws IllegalArgumentException if the {@code port} number is not valid.
   * @see #ConnectionTester(String, int)
   */
  public static ConnectionTester newConnectionTester(int port) {
    return new ConnectionTester(null, port);
  }

  /**
   * Factory method used to construct a new instance of {@link ConnectionTester} in order to test a connection
   * to the specified {@link String host} on the specified {@link Integer port}.
   *
   * @param host {@link String name} of the host to which the connection is bound.
   * @param port {@link Integer} value specifying the port number to which the connection is bound.
   * @return a new instance of {@link ConnectionTester} bound to the specified {@link String host}
   * and {@link Integer port}.
   * @throws IllegalArgumentException if the {@code port} number is not valid.
   * @see #ConnectionTester(String, int)
   */
  public static ConnectionTester newConnectionTester(String host, int port) {
    return new ConnectionTester(host, port);
  }

  private final AtomicBoolean connected = new AtomicBoolean(false);

  private final String host;

  private final int port;

  /**
   * Constructs a new instance of the {@link ConnectionTester} bound to the given {@link String host}
   * and {@link Integer port}.
   *
   * @param host {@link String name} of the host to which the connection is bound.
   * @param port {@link Integer} value specifying the port number to which the connection is bound.
   * @throws IllegalArgumentException if the {@code port} number is not valid.
   */
  public ConnectionTester(String host, int port) {
    assertThat(port).throwing(newIllegalArgumentException("[%d] is not a valid port number", port))
      .isGreaterThanAndLessThanEqualTo(0, ServicePort.MAX_PORT);

    this.host = (host == null || host.trim().isEmpty() ? null : host);
    this.port = port;
  }

  /**
   * Determines whether a network connection to the specified {@link #getHost()} and {@link #getPort()} was established.
   *
   * @return a boolean value indicating whether a network connection to the specified {@link #getHost()}
   * and {@link #getPort()} was successfully established.
   * @see #isNotConnected()
   */
  public boolean isConnected() {
    return this.connected.get();
  }

  /**
   * Determines whether a network connection to the specified {@link #getHost()} and {@link #getPort()} was established.
   *
   * @return a boolean value indicating whether a network connection to the specified {@link #getHost()}
   * and {@link #getPort()} was successfully established.
   * @see #isConnected()
   */
  public boolean isNotConnected() {
    return !isConnected();
  }

  /**
   * Sets whether a network connection to the specified {@link #getHost()} and {@link #getPort()} could be established.
   *
   * @param connected boolean value indicating whether a network connection to the specified {@link #getHost()}
   * and {@link #getPort()} was successfully established.
   * @return this {@link ConnectionTester}.
   * @see #isNotConnected()
   * @see #isConnected()
   */
  protected ConnectionTester setConnected(boolean connected) {
    this.connected.set(connected);
    return this;
  }

  /**
   * Return the {@link String name} of the host tested in the connection.
   *
   * @return the {@link String name} of the host tested in the connection.
   * @see #getPort()
   */
  public String getHost() {
    return this.host;
  }

  /**
   * Return the {@link Integer port number} tested in the connection.
   *
   * @return the {@link Integer port number} tested in the connection.
   * @see #getHost()
   */
  public int getPort() {
    return this.port;
  }

  /**
   * Executes the test.
   *
   * @return a boolean value indicating if the test was successful.
   * @see #test()
   */
  @Override
  public boolean evaluate() {
    return test();
  }

  /**
   * Resets the state of the {@link #isConnected() connected property} and retests the connection.
   *
   * @return a boolean value indicating whether a connection to the specified network {@link #getHost()}
   * and {@link #getPort()} was successful.
   * @see #setConnected(boolean)
   * @see #test()
   */
  public boolean retest() {
    return setConnected(false).test();
  }

  /**
   * Tests whether a connection to the specified network {@link #getHost()} and {@link #getPort()} can be established.
   *
   * @return a boolean value indicating whether a connection to the specified network {@link #getHost()}
   * and {@link #getPort()} was successful.
   * @see java.net.Socket#isConnected()
   * @see org.cp.elements.net.NetworkUtils#newSocketAddress(String, int)
   * @see #newSocket()
   * @see #getHost()
   * @see #getPort()
   */
  @Override
  public boolean test() {

    if (isNotConnected()) {
      try (Socket socket = newSocket()) {
        socket.setReuseAddress(SO_REUSEADDR);
        socket.connect(newSocketAddress(getHost(), getPort()), intValue(CONNECT_TIMEOUT));
        setConnected(socket.isConnected());
      }
      catch (IOException ignore) { }
    }

    return isConnected();
  }

  /**
   * Constructs a new, uninitialized {@link Socket}.
   *
   * @return a new, uninitialized {@link Socket}.
   * @see java.net.Socket#Socket()
   */
  protected Socket newSocket() {
    return new Socket();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy