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

org.jfxvnc.net.rfb.VncConnection Maven / Gradle / Ivy

The newest version!
package org.jfxvnc.net.rfb;

import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

import javax.annotation.PreDestroy;

import org.jfxvnc.net.rfb.codec.ProtocolInitializer;
import org.jfxvnc.net.rfb.render.DefaultProtocolConfiguration;
import org.jfxvnc.net.rfb.render.ProtocolConfiguration;
import org.jfxvnc.net.rfb.render.RenderProtocol;
import org.slf4j.LoggerFactory;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;

public class VncConnection {

  private final static org.slf4j.Logger logger = LoggerFactory.getLogger(VncConnection.class);

  private static final int DEF_CONNECT_PORT = 5900;
  private static final int DEF_LISTENING_PORT = 5500;

  private final ProtocolConfiguration config;

  private final ReadOnlyBooleanWrapper connected = new ReadOnlyBooleanWrapper(false);
  private final ReadOnlyBooleanWrapper connecting = new ReadOnlyBooleanWrapper(false);

  private final ThreadFactory executor;

  private NioEventLoopGroup workerGroup;

  private RenderProtocol render;

  private NioEventLoopGroup bossGroup;

  public VncConnection() {
    this(new ThreadFactory() {
      @Override
      public Thread newThread(Runnable r) {
        Thread t = new Thread(r);
        t.setName("vnc-connection-" + t.getId());
        t.setDaemon(true);
        return t;
      }
    });
  }

  public VncConnection(ThreadFactory executor) {
    this.executor = Objects.requireNonNull(executor);
    this.config = new DefaultProtocolConfiguration();
  }

  public ProtocolConfiguration getConfiguration() {
    return this.config;
  }

  public void setRenderProtocol(RenderProtocol render) {
    this.render = Objects.requireNonNull(render);
  }

  public void addFaultListener(Consumer l) {}

  public void removeFaultListener(Consumer l) {}

  public CompletableFuture connect() {
    shutdown();
    CompletableFuture future = new CompletableFuture<>();

    workerGroup = new NioEventLoopGroup(2, executor);

    connecting.set(true);

    String host = config.hostProperty().get();
    int port = config.portProperty().get() > 0 ? config.portProperty().get() : DEF_CONNECT_PORT;

    Bootstrap b = new Bootstrap();
    b.group(workerGroup);
    b.channel(NioSocketChannel.class);
    b.option(ChannelOption.SO_KEEPALIVE, true);
    b.option(ChannelOption.TCP_NODELAY, true);
    b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);

    b.handler(new ProtocolInitializer(render, config));

    logger.debug("try to connect to {}:{}", host, port);
    b.connect(host, port).addListener((ChannelFuture in) -> {
      connecting.set(false);
      connected.set(in.isSuccess());
      if (!in.isSuccess()) {
        future.completeExceptionally(in.cause());
        return;
      }
      future.complete(this);
    });

    return future;
  }

  public CompletableFuture startListeningMode() {

    shutdown();
    CompletableFuture future = new CompletableFuture<>();
    connected.set(true);

    bossGroup = new NioEventLoopGroup(1, executor);
    workerGroup = new NioEventLoopGroup(2, executor);

    ServerBootstrap b = new ServerBootstrap();
    b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100);

    b.childHandler(new ProtocolInitializer(render, config));
    int port = config.listeningPortProperty().get() > 0 ? config.listeningPortProperty().get() : DEF_LISTENING_PORT;

    b.bind(port).addListener(l -> {
      logger.debug("wait for incoming connection request on port: {}..", port);
      connected.set(l.isSuccess());
    }).addListener((ChannelFuture in) -> {
      if (!in.isSuccess()) {
        connecting.set(false);
        connected.set(false);
        future.completeExceptionally(in.cause());
      }
      future.complete(this);
    });

    return future;

  }

  @PreDestroy
  private void shutdown() {
    if (workerGroup != null && !workerGroup.isTerminated()) {
      workerGroup.shutdownGracefully(2, 5, TimeUnit.SECONDS);
    }
    if (bossGroup != null && !bossGroup.isTerminated()) {
      bossGroup.shutdownGracefully(2, 5, TimeUnit.SECONDS);
    }
  }

  public CompletableFuture disconnect() {

    if (!isConnected()) {
      return CompletableFuture.completedFuture(this);
    }
    connecting.set(false);
    connected.set(false);
    return CompletableFuture.supplyAsync(() -> {
      shutdown();
      return this;
    });

  }

  public boolean isConnected() {
    return connected.get();
  }

  public boolean isConnecting() {
    return connecting.get();
  }

  public ReadOnlyBooleanProperty connectedProperty() {
    return connected.getReadOnlyProperty();
  }

  public ReadOnlyBooleanProperty connectingProperty() {
    return connecting.getReadOnlyProperty();
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy