package eu.hgross.blaubot.ethernet; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.util.Arrays; import java.util.List; import eu.hgross.blaubot.core.IBlaubotAdapter; import eu.hgross.blaubot.core.IBlaubotConnection; import eu.hgross.blaubot.core.IBlaubotDevice; import eu.hgross.blaubot.core.acceptor.ConnectionMetaDataDTO; import eu.hgross.blaubot.core.acceptor.IBlaubotIncomingConnectionListener; import eu.hgross.blaubot.core.acceptor.discovery.BeaconMessage; import eu.hgross.blaubot.core.acceptor.discovery.IBlaubotBeaconStore; import eu.hgross.blaubot.core.connector.IBlaubotConnector; import eu.hgross.blaubot.core.connector.IncompatibleBlaubotDeviceException; import eu.hgross.blaubot.core.statemachine.BlaubotAdapterHelper; import eu.hgross.blaubot.util.Log; /** * Connector for ethernet * * @author Henning Gross {@literal (mail.to@henning-gross.de)} * */ public class BlaubotEthernetConnector implements IBlaubotConnector { private static final String LOG_TAG = "BlaubotEthernetConnector"; private static final List<String> SUPPORTED_ACCEPTOR_TYPES = Arrays.asList(EthernetConnectionMetaDataDTO.ACCEPTOR_TYPE); private final IBlaubotAdapter adapter; private final IBlaubotDevice ownDevice; private IBlaubotIncomingConnectionListener incomingConnectionListener; private IBlaubotBeaconStore beaconStore; public BlaubotEthernetConnector(IBlaubotAdapter blaubotEthernetAdapter, IBlaubotDevice ownDevice) { this.ownDevice = ownDevice; this.adapter = blaubotEthernetAdapter; } @Override public IBlaubotAdapter getAdapter() { return adapter; } @Override public void setBeaconStore(IBlaubotBeaconStore beaconStore) { this.beaconStore = beaconStore; } @Override public void setIncomingConnectionListener(IBlaubotIncomingConnectionListener acceptorConnectorListener) { this.incomingConnectionListener = acceptorConnectorListener; } @Override public IBlaubotConnection connectToBlaubotDevice(IBlaubotDevice blaubotDevice) { final String uniqueDeviceID = blaubotDevice.getUniqueDeviceID(); final List<ConnectionMetaDataDTO> lastKnownConnectionMetaData = beaconStore.getLastKnownConnectionMetaData(uniqueDeviceID); if(lastKnownConnectionMetaData == null) { if(Log.logErrorMessages()) { Log.e(LOG_TAG, "Could not get connection meta data for unique device id " + uniqueDeviceID); } return null; } // take the first supported acceptor, if any final List<ConnectionMetaDataDTO> supportedAcceptors = BlaubotAdapterHelper.filterBySupportedAcceptorTypes(lastKnownConnectionMetaData, getSupportedAcceptorTypes()); if(supportedAcceptors.isEmpty()) { if(Log.logErrorMessages()) { Log.e(LOG_TAG, "No supported acceptors in meta data to connect to " + uniqueDeviceID + " unfiltered list: " + lastKnownConnectionMetaData); } throw new IncompatibleBlaubotDeviceException(blaubotDevice + " could not get acceptor meta data for this device."); } EthernetConnectionMetaDataDTO ethernetConnectionMetaData = new EthernetConnectionMetaDataDTO(supportedAcceptors.get(0)); if (Log.logDebugMessages()) { Log.d(LOG_TAG, "Chosen acceptor to connect to: " + ethernetConnectionMetaData); Log.d(LOG_TAG, "Last DiscoveryEvent for this deviceId: "+beaconStore.getLastDiscoveryEvent(uniqueDeviceID)); } return connectToBlaubotDevice(blaubotDevice, ethernetConnectionMetaData); } /** * Connects to the given device using the given connection params. * This is a specialized method to bypass some validations to enable the re-use of this connector as a delegate. * * @param blaubotDevice the device to connect to * @param ethernetConnectionMetaData the connection meta data to use for the connection * @return the connection or null, if failed */ public IBlaubotConnection connectToBlaubotDevice(IBlaubotDevice blaubotDevice, EthernetConnectionMetaDataDTO ethernetConnectionMetaData) { int remoteAcceptorPort = ethernetConnectionMetaData.getAcceptorPort(); final String ipAddress = ethernetConnectionMetaData.getIpAddress(); // connect - other side is EthernetAcceptor, see there Socket remoteSocket = null; final int connectionTimeout = adapter.getBlaubotAdapterConfig().getConnectionTimeout(); long start = System.currentTimeMillis(); try { try { InetAddress remoteAddress = InetAddress.getByName(ipAddress); remoteSocket = new Socket(); remoteSocket.connect(new InetSocketAddress(remoteAddress, remoteAcceptorPort), connectionTimeout); BlaubotEthernetUtils.sendOwnUniqueIdThroughSocket(ownDevice, remoteSocket); BlaubotEthernetConnection connection = new BlaubotEthernetConnection(blaubotDevice, remoteSocket); // send our message final BeaconMessage currentBeaconMessage = adapter.getBlaubot().getConnectionStateMachine().getBeaconService().getCurrentBeaconMessage(); connection.write(currentBeaconMessage.toBytes()); if (Log.logDebugMessages()) { long diff = System.currentTimeMillis() - start; Log.d(LOG_TAG, "Connection successful, took " + diff + " ms"); } if (incomingConnectionListener != null) { incomingConnectionListener.onConnectionEstablished(connection); } return connection; } catch (UnknownHostException e) { if (Log.logErrorMessages()) { Log.e(LOG_TAG, "Could not extract InetAddress from: " + ipAddress); } throw e; } catch (SocketTimeoutException e) { if (Log.logErrorMessages()) { Log.e(LOG_TAG, "Timeout: Could not connect socket to remote device (after " + connectionTimeout + " ms)"); } } } catch (IOException e) { if(Log.logWarningMessages()) { Log.w(LOG_TAG, "Could not connect or write to " + ipAddress + ":" + remoteAcceptorPort + " (" + e.getMessage() + ")"); } if(remoteSocket != null) { try { remoteSocket.close(); } catch (IOException e1) { } } } if(Log.logWarningMessages()) { long diff = System.currentTimeMillis() - start; Log.w(LOG_TAG, "Failed to connect to " + ipAddress + ":" + remoteAcceptorPort + " after " + diff + " ms"); } return null; } @Override public List<String> getSupportedAcceptorTypes() { return SUPPORTED_ACCEPTOR_TYPES; } }