package org.dcache.services.info.conduits; import com.google.common.net.InetAddresses; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Required; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import org.dcache.services.info.serialisation.StateSerialiser; /** * Information Exporter class.<br> * This class is instantiated by the <code>InfoCollector</code> to send * over a plain TCP socket a <code>Schema</code> object that carries out * dynamic information from dCache.<br><br> * Also this class is independent from the particular implementation of * Schema used. As matter of fact, this class serialises a generic Schema * object over a socket. It's a job of the client to know what particular * implementation of Schema was sent.<br><br> * Note that client needs only to know the specializing class of the Schema. */ public class XmlConduit extends AbstractThreadedConduit { private static final Logger LOGGER = LoggerFactory.getLogger(XmlConduit.class); /** TCP port that the server listens on */ private int _port; /** TCP backlog */ private int _backlog; /** IP address to bind to */ private String _bindAddress; /** Server Socket reference */ private ServerSocket _svr_skt; /** Our serialiser for the current dCache state */ private StateSerialiser _serialiser; @Required public void setSerialiser(StateSerialiser serialiser) { _serialiser = serialiser; } @Required public void setPort(int port) { _port = port; } public int getPort() { return _port; } @Required public void setBacklog(int backlog) { _backlog = backlog; } public int getBacklog() { return _backlog; } @Required public void setBindAddress(String address) { _bindAddress = address; } public String getBindAddress() { return _bindAddress; } @Override public void enable() { try { _svr_skt = new ServerSocket(_port, _backlog, InetAddresses.forString(_bindAddress)); } catch (IOException e) { Thread.currentThread().interrupt(); return; } catch (SecurityException e) { LOGGER.error("security issue creating port {}", _port, e); return; } super.enable(); // start the thread. } @Override void triggerBlockingActivityToReturn() { if (_svr_skt == null) { return; } try { _svr_skt.close(); } catch (IOException e) { LOGGER.error("Problem closing server socket", e); } finally { _svr_skt = null; } } /** * Wait for an incoming connection to the listening socket. When * one is received, send it the XML serialisation of our current state. */ @Override void blockingActivity() { Socket skt = null; try { skt = _svr_skt.accept(); } catch (SocketException e) { if (_svr_skt != null && (this._should_run || !_svr_skt.isClosed())) { LOGGER.error("accept() failed", e); } } catch (IOException e) { Thread.currentThread().interrupt(); return; } catch (SecurityException e) { LOGGER.error ("accept() failed for security reasons", e); return; } if (skt != null) { LOGGER.trace("Incoming connection from {}", skt); try { _callCount++; String data = _serialiser.serialise(); skt.getOutputStream().write(data.getBytes()); } catch (IOException e) { LOGGER.error("failed to write XML data", e); } catch (Exception e) { LOGGER.error("unknown failure writing XML data", e); } finally { try { skt.close(); } catch (IOException e) { Thread.currentThread().interrupt(); } } } } }