/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.transport.socket; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import org.fudgemsg.FudgeContext; import org.fudgemsg.FudgeField; import org.fudgemsg.FudgeMsg; import org.fudgemsg.MutableFudgeMsg; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.Lifecycle; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.transport.EndPointDescriptionProvider; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.monitor.ReportingInputStream; import com.opengamma.util.monitor.ReportingOutputStream; /** * * */ public abstract class AbstractSocketProcess implements Lifecycle, EndPointDescriptionProvider { private static final Logger s_logger = LoggerFactory.getLogger(AbstractSocketProcess.class); private Collection<InetAddress> _inetAddresses; private int _portNumber; private boolean _started; private Socket _socket; /** * @return the inetAddress */ public Collection<InetAddress> getInetAddresses() { return Collections.unmodifiableCollection(_inetAddresses); } /** * @param inetAddress the inetAddress to set */ public void setInetAddress(InetAddress inetAddress) { _inetAddresses = Collections.singleton(inetAddress); } public void setInetAddresses(Collection<InetAddress> inetAddresses) { _inetAddresses = new ArrayList<InetAddress>(inetAddresses); } public void setAddress(final String host) throws UnknownHostException { setInetAddresses(Arrays.asList(InetAddress.getAllByName(host))); } /** * @return the portNumber */ public int getPortNumber() { return _portNumber; } /** * @param portNumber the portNumber to set */ public void setPortNumber(int portNumber) { _portNumber = portNumber; } /** * Set the connection parameters based on the end point description of a server. * * @param endPoint An end-point description. */ public void setServer(final FudgeMsg endPoint) { ArgumentChecker.notNull(endPoint, "endPoint"); if (!SocketEndPointDescriptionProvider.TYPE_VALUE.equals(endPoint.getString(SocketEndPointDescriptionProvider.TYPE_KEY))) { throw new IllegalArgumentException("End point is not a ServerSocket - " + endPoint); } final Collection<InetAddress> addresses = new HashSet<InetAddress>(); for (FudgeField addr : endPoint.getAllByName(SocketEndPointDescriptionProvider.ADDRESS_KEY)) { final String host = endPoint.getFieldValue(String.class, addr); try { addresses.addAll(Arrays.asList(InetAddress.getAllByName(host))); } catch (UnknownHostException e) { s_logger.warn("Unknown host {}", host); } } setPortNumber(endPoint.getInt(SocketEndPointDescriptionProvider.PORT_KEY)); setInetAddresses(addresses); s_logger.debug("End point {} resolved to {}:{}", new Object[] {endPoint, getInetAddresses(), getPortNumber() }); } protected Socket getSocket() { return _socket; } protected void startIfNecessary() { if (!isRunning()) { s_logger.debug("Starting implicitly as start() was not called before use."); start(); } } @Override public synchronized boolean isRunning() { return _started; } @Override public synchronized void start() { ArgumentChecker.notNullInjected(getInetAddresses(), "Remote InetAddress"); ArgumentChecker.isTrue(getPortNumber() > 0, "Must specify valid portNumber property"); if (_started && (_socket != null)) { s_logger.warn("Already connected to {}", _socket.getRemoteSocketAddress()); } else { openRemoteConnection(); _started = true; } } protected synchronized void openRemoteConnection() { s_logger.info("Opening remote connection to {}:{}", getInetAddresses(), getPortNumber()); OutputStream os = null; InputStream is = null; for (InetAddress addr : getInetAddresses()) { try { _socket = new Socket(); _socket.connect(new InetSocketAddress(addr, getPortNumber()), 3000); s_logger.debug("Connected to {}:{}", addr, getPortNumber()); os = _socket.getOutputStream(); is = _socket.getInputStream(); break; } catch (IOException ioe) { s_logger.debug("Couldn't connect to {}:{}", addr, getPortNumber()); if (_socket != null) { try { _socket.close(); } catch (IOException e) { // Ignore } _socket = null; } } } if (_socket == null) { throw new OpenGammaRuntimeException("Unable to open remote connection to " + getInetAddresses() + ":" + getPortNumber()); } is = new ReportingInputStream(s_logger, _socket.getRemoteSocketAddress().toString(), is); os = new ReportingOutputStream(s_logger, _socket.getRemoteSocketAddress().toString(), os); socketOpened(_socket, new BufferedOutputStream(os), new BufferedInputStream(is)); } @Override public synchronized void stop() { if (_started) { if (_socket != null) { if (_socket.isConnected()) { try { _socket.close(); } catch (IOException e) { s_logger.warn("Unable to close connected socket to {}", new Object[] {_socket.getRemoteSocketAddress() }, e); } } _socket = null; } _started = false; socketClosed(); } else { s_logger.warn("Already stopped {}:{}", getInetAddresses(), getPortNumber()); } } protected boolean exceptionForcedByClose(final Exception e) { return (e instanceof SocketException) && "Socket closed".equals(e.getMessage()); } protected abstract void socketOpened(Socket socket, BufferedOutputStream os, BufferedInputStream is); protected void socketClosed() { } @Override public FudgeMsg getEndPointDescription(final FudgeContext fudgeContext) { final MutableFudgeMsg desc = fudgeContext.newMessage(); desc.add(SocketEndPointDescriptionProvider.TYPE_KEY, SocketEndPointDescriptionProvider.TYPE_VALUE); if (getInetAddresses() != null) { for (InetAddress addr : getInetAddresses()) { desc.add(SocketEndPointDescriptionProvider.ADDRESS_KEY, addr.getHostAddress()); } } desc.add(SocketEndPointDescriptionProvider.PORT_KEY, getPortNumber()); return desc; } }