com.addc.commons.slp.NetworkManagerImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of addc-slp Show documentation
Show all versions of addc-slp Show documentation
The addc-slp library supplies client classes for registering objects with a Service Location Protocol Daemon and
for looking theses objects up later.
package com.addc.commons.slp;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.addc.commons.Mutex;
import com.addc.commons.slp.configuration.SLPConfig;
import com.addc.commons.slp.messages.SLPMessage;
import com.addc.commons.slp.messages.ServiceRequest;
import com.addc.commons.slp.messages.UAMessage;
/**
* NetworkManagerImpl is a singleton which controls the sending and receiving of
* datagram packets for UAs, and the TCP connection for SAs.
*/
public final class NetworkManagerImpl implements NetworkManager {
private static final Logger LOGGER= LoggerFactory.getLogger(NetworkManagerImpl.class);
private int nextXid;
private final ByteArrayOutputStream saBaos;
private final DataOutputStream saDaos;
private final SLPConfig config;
private final Mutex mutex= new Mutex();
private List daAddresses;
/**
* Create a new NetworkManagerImpl
*
* @param config
* the configuration
*/
public NetworkManagerImpl(SLPConfig config) {
this.config= config;
saBaos= new ByteArrayOutputStream();
saDaos= new DataOutputStream(saBaos);
SecureRandom sr= new SecureRandom();
nextXid= sr.nextInt(Short.MAX_VALUE);
findDA();
}
@Override
public Socket connectToSlpd() throws IOException {
return new Socket("localhost", config.getPort());
}
@Override
public void findDA() {
daAddresses= Collections.synchronizedList(new ArrayList<>(config.getDaAddresses()));
if (daAddresses.isEmpty()) {
try {
UAMessage msg= new ServiceRequest(config, new ServiceType(SLPConstants.SLP_DA_TYPE));
DAAdvertEnumeration enumerator= new DAAdvertEnumeration(config, this, msg);
if (enumerator.hasMoreElements()) {
String str= enumerator.next();
String da= str.substring(26);
LOGGER.info("Received {}, add DA address {}", str, da);
daAddresses.add(da);
}
enumerator.destroy();
} catch (ServiceLocationException e) {
LOGGER.error("Caught an exception discovering DA", e);
}
}
}
@Override
public InetAddress getDaAddress() {
InetAddress address= null;
synchronized (daAddresses) {
while (!daAddresses.isEmpty()) {
try {
String daAddr= daAddresses.get(0);
if (daAddr.startsWith(SLPConstants.SLP_DA_TYPE)) {
daAddr= daAddr.substring(26);
}
address= InetAddress.getByName(daAddr);
LOGGER.debug("Using DA address {}", address.getHostAddress());
return address;
} catch (UnknownHostException e) {
LOGGER.debug("Failed to get da address.", e);
daAddresses.remove(0);
}
}
}
try {
address= InetAddress.getByName(SLPConstants.SLP_MCAST_ADDRESS);
LOGGER.info("Using muticast address {}", address);
} catch (UnknownHostException e) {
LOGGER.warn(e.getMessage());
}
return address;
}
@Override
public int nextXid() {
int id;
synchronized (mutex) {
if (nextXid == Short.MAX_VALUE) {
nextXid= 1;
}
id= nextXid++;
}
return id;
}
@Override
public void saMessage(SLPMessage message) throws ServiceLocationException {
Socket saSock= null;
try {
message.setXid(nextXid());
DataInputStream istream= null;
DataOutputStream ostream= null;
try {
saSock= connectToSlpd();
ostream= new DataOutputStream(saSock.getOutputStream());
istream= new DataInputStream(saSock.getInputStream());
} catch (IOException e) {
LOGGER.error("Cannot connect to slpd. SAs must have slpd running on the localhost.", e);
throw new ServiceLocationException(
"Cannot connect to slpd. SAs must have slpd running on the localhost.", e,
SLPConstants.NETWORK_ERROR);
}
saBaos.reset();
message.writeMessage(saDaos, true);
byte[] buf= saBaos.toByteArray();
ostream.write(buf);
ostream.flush();
int errorCode= saReadAck(message.getXid(), istream);
if (errorCode != 0) {
LOGGER.error("Error sending SA Message {}", errorCode);
throw new ServiceLocationException("Error sending SA Message", errorCode);
}
} catch (IOException e) {
LOGGER.error("Error sending message", e);
throw new ServiceLocationException("Could not send message", e, SLPConstants.NETWORK_ERROR);
} finally {
if (saSock != null) {
try {
saSock.close();
} catch (IOException e) {
LOGGER.debug("Error closing socket", e);
}
saSock= null;
}
}
}
/**
* Read the ack response to an SA message.
*
* @param xid
* The XID
* @param istream
* the stream to read
* @return The ACK byte.
* @throws IOException
* If there is a socket error.
*/
private int saReadAck(int xid, DataInputStream istream) throws IOException {
istream.readByte();
istream.readByte();
if (istream.skip(8) != 8L) {
LOGGER.warn("Skip did not advance 8 bytes");
throw new IOException("Skip did not advance 8 bytes");
}
int recvXid= istream.readShort();
if (recvXid != xid) {
LOGGER.warn("Received non matching XID expected {} but received {}", xid, recvXid);
return -1;
}
int langtagLen= istream.readShort() & 0xFFFF;
if (langtagLen != istream.skip(langtagLen)) {
String msg= MessageFormat.format("Failed to skip {0} bytes", langtagLen);
LOGGER.warn(msg);
throw new IOException(msg);
}
return istream.readShort();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy