package dmg.cells.network; import com.google.common.net.HostAndPort; import com.google.common.util.concurrent.Uninterruptibles; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InterruptedIOException; import java.io.PrintWriter; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.channels.SocketChannel; import java.nio.channels.UnresolvedAddressException; import java.nio.channels.UnsupportedAddressTypeException; import java.util.Random; import java.util.concurrent.ExecutionException; import dmg.cells.nucleus.CellAdapter; import dmg.util.DummyStreamEngine; import dmg.util.StreamEngine; import org.dcache.alarms.AlarmMarkerFactory; import org.dcache.alarms.PredefinedAlarm; import org.dcache.util.Args; import org.dcache.util.NDC; public class LocationManagerConnector extends CellAdapter implements Runnable { private static final Logger _log = LoggerFactory.getLogger("org.dcache.cells.network"); private final String _domain; private final InetSocketAddress _address; private Thread _thread; private String _status = "disconnected"; private int _retries; public LocationManagerConnector(String cellName, String args) { super(cellName, "System", args); Args a = getArgs(); _domain = a.getOpt("domain"); HostAndPort where = HostAndPort.fromString(a.getOpt("where")); _address = new InetSocketAddress(where.getHostText(), where.getPort()); } @Override protected void started() { _thread = getNucleus().newThread(this, "TunnelConnector-" + _domain); _thread.start(); } private synchronized void setStatus(String s) { _status = s; } private synchronized String getStatus() { return _status; } private StreamEngine connect() throws IOException, InterruptedException { setStatus("Connecting to " + _address); Socket socket; try { socket = SocketChannel.open(_address).socket(); } catch (UnsupportedAddressTypeException e) { throw new IOException("Unsupported address type: " + _address, e); } catch (UnresolvedAddressException e) { throw new IOException("Unable to resolve " + _address, e); } catch (InterruptedIOException e) { throw e; } catch (IOException e) { throw new IOException("Failed to connect to " + _address + ": " + e, e); } socket.setKeepAlive(true); _log.info("Using clear text channel"); return new DummyStreamEngine(socket); } @Override public void run() { /* Thread for creating the tunnel. There is a grace period of * 4 to 20 seconds between connection attempts. The thread is * terminated by interrupting it. */ Args args = getArgs(); String name = getCellName() + '*'; Random random = new Random(); NDC.push(_address.toString()); try { while (true) { try { _retries++; LocationMgrTunnel tunnel = new LocationMgrTunnel(name, connect(), args); try { tunnel.start().get(); _retries = 0; setStatus("Connected"); getNucleus().join(tunnel.getCellName()); } finally { getNucleus().kill(tunnel.getCellName()); } } catch (InterruptedIOException e) { throw e; } catch (ExecutionException | IOException e) { _log.warn(AlarmMarkerFactory.getMarker(PredefinedAlarm.LOCATION_MANAGER_FAILURE, name, _domain, e.getMessage()), "Failed to connect to " + _domain + ": " + e.getMessage()); } setStatus("Sleeping"); long sleep = random.nextInt(16000) + 4000; _log.warn("Sleeping " + (sleep / 1000) + " seconds"); Thread.sleep(sleep); } } catch (InterruptedIOException | InterruptedException e) { Thread.currentThread().interrupt(); } finally { NDC.pop(); setStatus("Terminated"); } } public String toString() { return getStatus(); } @Override public void getInfo(PrintWriter pw) { pw.println("Location manager connector : " + getCellName()); pw.println("Status : " + getStatus()); pw.println("Retries : " + _retries); } @Override public void stopped() { if (_thread != null) { _thread.interrupt(); Uninterruptibles.joinUninterruptibly(_thread); } } }