/** * Copyright (C) 2014 zml (netevents@zachsthings.com) * * 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 com.zachsthings.netevents; import com.zachsthings.netevents.packet.DisconnectPacket; import com.zachsthings.netevents.packet.Packet; import com.zachsthings.netevents.packet.ServerIDPacket; import java.io.Closeable; import java.io.IOException; import java.net.SocketAddress; import java.nio.channels.SocketChannel; import java.nio.channels.UnresolvedAddressException; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; /** * Handles logic of connection management and teardown. * * Wraps connection a lot, but handles the logic of state tracking in {@link com.zachsthings.netevents.NetEventsPlugin} and autoreconnect. */ public class Forwarder implements Closeable { private final NetEventsPlugin plugin; private final AtomicReference<Connection> conn = new AtomicReference<>(); private SocketAddress reconnectAddress; private final AtomicReference<UUID> remoteServerUUID = new AtomicReference<>(); public Forwarder(NetEventsPlugin plugin) { this.plugin = plugin; } class ConnectionCloseListener implements Runnable { @Override public void run() { if (Forwarder.this.reconnectAddress != null) { plugin.getReconnectTask().schedule(Forwarder.this); } else { plugin.removeForwarder(Forwarder.this); } conn.set(null); } } public void connect(SocketAddress addr) throws IOException { remoteServerUUID.set(null); reconnectAddress = addr; final SocketChannel sock; try { sock = SocketChannel.open(addr); } catch (UnresolvedAddressException e) { throw new IOException("Unknown host: " + addr); } connect(sock); reconnectAddress = addr; } public void connect(SocketChannel chan) throws IOException { Connection.configureSocketChannel(chan); chan = plugin.getSocketWrapper().wrapSocket(chan); final Connection conn = Connection.open(this, chan); if (!this.conn.compareAndSet(null, conn)) { // Already been connected conn.close(); } else { // Successfully connected, now perform initialization conn.addCloseListener(new ConnectionCloseListener()); reconnectAddress = null; // Clear it out in case of previous connection write(new ServerIDPacket(plugin.getServerUUID())); plugin.debug("Connected to " + chan.getRemoteAddress()); } } public boolean isReconnectable() { return this.reconnectAddress != null; } public UUID getRemoteServerUUID() { return remoteServerUUID.get(); } public void setRemoteServerUUID(UUID remoteUid) { if (!remoteServerUUID.compareAndSet(null, remoteUid)) { throw new IllegalStateException("Server UUID has already been set for " + this); } } boolean reconnect() throws IOException { SocketAddress reconnectAddress = this.reconnectAddress; if (reconnectAddress != null) { connect(reconnectAddress); return true; } return false; } public void close() throws IOException { final Connection conn = this.conn.get(); reconnectAddress = null; if (conn != null) { conn.close(); } } public void write(Packet packet) { final Connection conn = this.conn.get(); if (conn != null) { conn.write(packet); } } public void disconnect(String reason) throws IOException { final Connection conn = this.conn.get(); if (conn != null) { plugin.getLogger().info("Disconnecting from " + conn + ": " + reason); conn.writeAndClose(new DisconnectPacket(reason, false)); } } public NetEventsPlugin getPlugin() { return plugin; } public boolean isActive() { return this.conn.get() != null; } public SocketAddress getRemoteAddress() { final Connection conn = this.conn.get(); if (conn != null) { return conn.getRemoteAddress(); } else { return reconnectAddress; } } @Override public String toString() { return "Forwarder{" + "conn=" + conn + ", reconnectAddress=" + reconnectAddress + '}'; } }