All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.addc.commons.slp.NetworkManagerImpl Maven / Gradle / Ivy

Go to download

The addc-slp library supplies client classes for registering objects with a Service Location Protocol Daemon and for looking theses objects up later.

There is a newer version: 2.6
Show newest version
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