2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * ident "%Z%%M% %I% %E% SMI"
2N/A *
2N/A * Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A *
2N/A */
2N/A
2N/A// Transact.java: Low level details of performing an SLP
2N/A// network transaction.
2N/A
2N/Apackage com.sun.slp;
2N/A
2N/Aimport java.util.*;
2N/Aimport java.net.*;
2N/Aimport java.io.*;
2N/A
2N/A/**
2N/A * Transact performs the low level details for transacting an SLP network
2N/A * query. Note that, in the future, this class may spin separate threads
2N/A * for DA requests as well.
2N/A */
2N/A
2N/Aclass Transact extends Object implements Runnable {
2N/A
2N/A // Cache of open TCP sockets.
2N/A
2N/A private static final Hashtable TCPSocketCache = new Hashtable();
2N/A
2N/A // SLP config object.
2N/A
2N/A protected static SLPConfig config = null;
2N/A
2N/A // Message to send.
2N/A
2N/A protected SrvLocMsg msgOut = null;
2N/A
2N/A // Vector of return values.
2N/A
2N/A protected Vector returns = null;
2N/A
2N/A // Timeout for multicast convergence. Varies if it's DA discovery or
2N/A // request multicast.
2N/A
2N/A protected int[] MSTimeouts;
2N/A
2N/A // Maximum results desired for multicast.
2N/A
2N/A protected int maxResults = 0;
2N/A
2N/A // Exception to throw.
2N/A
2N/A protected ServiceLocationException exErr = null;
2N/A
2N/A // Multicast address to use.
2N/A
2N/A protected InetAddress address = null;
2N/A
2N/A // If this is true, continue multicast after the first set of stuff
2N/A // is found. Exit when three tries have happened without finding
2N/A // anything.
2N/A
2N/A boolean continueAfterFound = false;
2N/A
2N/A /**
2N/A * Perform a query to the SLP network. The multicast query is performed
2N/A * in a separate thread for performance reasons. DAs having the
2N/A * same scope set are queried until one answers. These DAs form
2N/A * an equivalence class.
2N/A *
2N/A * @param daEquivClasses Vector of DATable.DARecord objects in the
2N/A * same equivalence clase w.r.t. scopes.
2N/A * @param uniMsg A unicast message to send.
2N/A * @param multiMsg A multicast message to send.
2N/A * @param address Multicast address to use.
2N/A * @return Vector of SrvLocMsg objects with results.
2N/A */
2N/A
2N/A static Vector
2N/A transactUA(Vector daEquivClasses,
2N/A SrvLocMsg uniMsg,
2N/A SrvLocMsg multiMsg,
2N/A InetAddress address)
2N/A throws ServiceLocationException {
2N/A
2N/A // If we need to multicast, then start the multicast thread.
2N/A
2N/A Vector ret = new Vector();
2N/A Thread multiThread = null;
2N/A Transact tracon = null;
2N/A
2N/A if (multiMsg != null) {
2N/A
2N/A // Create a new Transact multicast thread.
2N/A
2N/A // The final argument to the constructor of Transact determines
2N/A // whether to return after the first result or to continue to
2N/A // gather more than one result. The value to this field
2N/A // continueAfterFound MUST be set to 'true' or else multicast
2N/A // based discovery will find the first result, not all results,
2N/A // as it should.
2N/A tracon =
2N/A new Transact(multiMsg,
2N/A ret,
2N/A config.getMulticastTimeouts(),
2N/A config.getMaximumResults(),
2N/A address,
2N/A true); // continueAfterFound
2N/A
2N/A multiThread = new Thread(tracon);
2N/A
2N/A // Run it.
2N/A
2N/A multiThread.start();
2N/A
2N/A }
2N/A
2N/A // Go through the msgTable doing all the DAs.
2N/A
2N/A ServiceLocationException exx = null;
2N/A
2N/A if (daEquivClasses != null) {
2N/A exx =
2N/A transactUnicastMsg(daEquivClasses,
2N/A uniMsg,
2N/A ret,
2N/A config.getMaximumResults());
2N/A
2N/A }
2N/A
2N/A // Wait until the TransactConverge thread is done, if necessary.
2N/A
2N/A if (multiThread != null) {
2N/A
2N/A try {
2N/A multiThread.join();
2N/A
2N/A } catch (InterruptedException ex) {
2N/A
2N/A }
2N/A
2N/A }
2N/A
2N/A // If there was a problem in either the multicast thread or in
2N/A // the unicast call, throw an exception, but *only* if no
2N/A // results came back.
2N/A
2N/A if (ret.size() <= 0) {
2N/A
2N/A if (exx != null) {
2N/A short err = exx.getErrorCode();
2N/A
2N/A if (err != ServiceLocationException.VERSION_NOT_SUPPORTED &&
2N/A err != ServiceLocationException.INTERNAL_ERROR &&
2N/A err != ServiceLocationException.OPTION_NOT_SUPPORTED &&
2N/A err != ServiceLocationException.REQUEST_NOT_SUPPORTED) {
2N/A throw exx;
2N/A
2N/A }
2N/A
2N/A }
2N/A
2N/A if (tracon != null && tracon.exErr != null) {
2N/A short err = tracon.exErr.getErrorCode();
2N/A
2N/A if (err != ServiceLocationException.VERSION_NOT_SUPPORTED &&
2N/A err != ServiceLocationException.INTERNAL_ERROR &&
2N/A err != ServiceLocationException.OPTION_NOT_SUPPORTED &&
2N/A err != ServiceLocationException.REQUEST_NOT_SUPPORTED) {
2N/A throw tracon.exErr;
2N/A
2N/A }
2N/A }
2N/A }
2N/A
2N/A
2N/A // Return the result to the client.
2N/A
2N/A return ret;
2N/A
2N/A }
2N/A
2N/A /**
2N/A * Transact a message with DAs. Put the returned SrvLocMsg
2N/A * object into the Vector ret.
2N/A *
2N/A * @param daEquivClasses Vector of DATable.DARecord objects in the
2N/A * same equivalence clase w.r.t. scopes.
2N/A * @param msg SrvLocMsg Message to send.
2N/A * @param ret Vector for returns.
2N/A * @param maxResults Maximum results expected.
2N/A * @return A ServiceLocationException object if an exception occured.
2N/A * @exception ServiceLocationException
2N/A * If results cannot be obtained in the timeout interval
2N/A * specified in the 'config.' or
2N/A * If networking resources cannot be obtained or used
2N/A * effectively.
2N/A */
2N/A static ServiceLocationException
2N/A transactUnicastMsg(Vector daEquivClasses,
2N/A SrvLocMsg msg,
2N/A Vector ret,
2N/A int maxResults) {
2N/A
2N/A // Get the config object if we need it.
2N/A
2N/A if (config == null) {
2N/A config = SLPConfig.getSLPConfig();
2N/A
2N/A }
2N/A
2N/A DatagramSocket ds = null;
2N/A int i, n = daEquivClasses.size();
2N/A ServiceLocationException exx = null;
2N/A InetAddress addr = null;
2N/A int numReplies = 0;
2N/A DATable daTable = DATable.getDATable();
2N/A
2N/A try {
2N/A
2N/A // Go through the DA address equivalence classes we need
2N/A // to query.
2N/A
2N/A for (i = 0; i < n && numReplies < maxResults; i++) {
2N/A
2N/A DATable.DARecord rec =
2N/A (DATable.DARecord)daEquivClasses.elementAt(i);
2N/A Vector daAddresses = (Vector)rec.daAddresses.clone();
2N/A
2N/A // Get a new outgoing socket.
2N/A
2N/A if (ds == null) {
2N/A ds = new DatagramSocket();
2N/A
2N/A }
2N/A
2N/A // Go through the DA addresses until we get a reply from one.
2N/A
2N/A Enumeration en = daAddresses.elements();
2N/A SrvLocHeader mhdr = msg.getHeader();
2N/A
2N/A while (en.hasMoreElements()) {
2N/A
2N/A try {
2N/A
2N/A addr = (InetAddress)en.nextElement();
2N/A
2N/A if (config.traceDATraffic()) {
2N/A config.writeLog("sending_da_trace",
2N/A new Object[] {
2N/A Integer.toHexString(mhdr.xid),
2N/A addr});
2N/A
2N/A }
2N/A
2N/A // Get the reply message if any.
2N/A
2N/A SrvLocMsg rply = transactDatagramMsg(ds, addr, msg);
2N/A
2N/A if (!filterRply(msg, rply, addr)) {
2N/A continue;
2N/A
2N/A }
2N/A
2N/A SrvLocHeader rhdr = rply.getHeader();
2N/A
2N/A if (config.traceDATraffic()) {
2N/A config.writeLog("reply_da_trace",
2N/A new Object[] {
2N/A Integer.toHexString(rhdr.xid),
2N/A addr});
2N/A
2N/A }
2N/A
2N/A // If overflow, try TCP.
2N/A
2N/A if (rhdr.overflow) {
2N/A if (config.traceDATraffic()) {
2N/A config.writeLog("tcp_send_da_trace",
2N/A new Object[] {
2N/A Integer.toHexString(mhdr.xid),
2N/A addr});
2N/A
2N/A }
2N/A
2N/A rply = transactTCPMsg(addr, msg, false);
2N/A
2N/A if (config.traceDATraffic()) {
2N/A config.writeLog("tcp_reply_da_trace",
2N/A new Object[] {
2N/A (msg == null ? "<null>":
2N/A Integer.toHexString(mhdr.xid)),
2N/A addr});
2N/A
2N/A }
2N/A
2N/A if (rply == null) {
2N/A continue;
2N/A
2N/A }
2N/A
2N/A }
2N/A
2N/A // Increment number of replies we received.
2N/A
2N/A SrvLocHeader hdr = rply.getHeader();
2N/A
2N/A numReplies += hdr.iNumReplies;
2N/A
2N/A // Add to return vector.
2N/A
2N/A ret.addElement(rply);
2N/A
2N/A // Break out of the loop, since we only need one in
2N/A // this equivalence class.
2N/A
2N/A break;
2N/A
2N/A } catch (ServiceLocationException ex) {
2N/A
2N/A config.writeLog("da_exception_trace",
2N/A new Object[] {
2N/A new Short(ex.getErrorCode()),
2N/A addr,
2N/A ex.getMessage()});
2N/A
2N/A // In case we are querying more than one DA, we
2N/A // save th exception, returning it to the caller to
2N/A // decide if it should be thrown. We ignore DA_BUSY,
2N/A // though, since the DA may free up later.
2N/A
2N/A short errCode = ex.getErrorCode();
2N/A
2N/A if (errCode != ServiceLocationException.DA_BUSY) {
2N/A exx = ex;
2N/A
2N/A }
2N/A
2N/A // If the error code is NETWORK_TIMED_OUT, then remove
2N/A // this DA from the DA table. If it's just down
2N/A // temporarily, we'll get it next time we go to
2N/A // the server to get the DA addresses.
2N/A
2N/A if (errCode ==
2N/A ServiceLocationException.NETWORK_TIMED_OUT) {
2N/A
2N/A if (config.traceDATraffic()) {
2N/A config.writeLog("da_drop",
2N/A new Object[] {
2N/A addr, rec.scopes});
2N/A
2N/A }
2N/A
2N/A daTable.removeDA(addr, rec.scopes);
2N/A
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A } catch (SocketException ex) {
2N/A exx =
2N/A new ServiceLocationException(
2N/A ServiceLocationException.NETWORK_ERROR,
2N/A "socket_creation_failure",
2N/A new Object[] {addr, ex.getMessage()});
2N/A
2N/A } finally {
2N/A
2N/A // Clean up socket.
2N/A
2N/A if (ds != null) {
2N/A ds.close();
2N/A }
2N/A }
2N/A
2N/A return exx;
2N/A }
2N/A
2N/A /**
2N/A * Transact a message via. UDP. Try a maximum of three times if
2N/A * a timeout.
2N/A *
2N/A * @param ds The datagram socket to use.
2N/A * @param addr The DA to contact.
2N/A * @param msg The message to send.
2N/A * @return The SrvLocMsg returned or null if none.
2N/A * @exception ServiceLocationException Due to errors in parsing message.
2N/A */
2N/A
2N/A static private SrvLocMsg
2N/A transactDatagramMsg(DatagramSocket ds, InetAddress addr, SrvLocMsg msg)
2N/A throws ServiceLocationException {
2N/A
2N/A SrvLocMsg rply = null;
2N/A byte[] outbuf = getBytes(msg, false, false);
2N/A byte[] inbuf = new byte[Defaults.iReadMaxMTU];
2N/A
2N/A // Construct the datagram packet to send.
2N/A
2N/A DatagramPacket dpReply =
2N/A new DatagramPacket(inbuf, inbuf.length);
2N/A DatagramPacket dpRequest =
2N/A new DatagramPacket(outbuf, outbuf.length, addr, Defaults.iSLPPort);
2N/A int[] timeouts = config.getDatagramTimeouts();
2N/A
2N/A // Resend for number of timeouts in timeout interval.
2N/A
2N/A int i;
2N/A
2N/A for (i = 0; i < timeouts.length; i++) {
2N/A
2N/A // Catch timeout and IO errors.
2N/A
2N/A try {
2N/A
2N/A ds.setSoTimeout(timeouts[i]);
2N/A ds.send(dpRequest);
2N/A ds.receive(dpReply);
2N/A
2N/A // Process result into a reply object.
2N/A
2N/A DataInputStream dis =
2N/A new DataInputStream(
2N/A new ByteArrayInputStream(dpReply.getData()));
2N/A
2N/A rply = internalize(dis, addr);
2N/A break;
2N/A
2N/A } catch (InterruptedIOException ex) {
2N/A
2N/A // Did not get it on the first timeout, try again.
2N/A
2N/A if (config.traceDrop()|| config.traceDATraffic()) {
2N/A config.writeLog("udp_timeout",
2N/A new Object[] {addr});
2N/A
2N/A }
2N/A
2N/A continue;
2N/A
2N/A } catch (IOException ex) {
2N/A Object[] message = {addr, ex.getMessage()};
2N/A
2N/A if (config.traceDrop() || config.traceDATraffic()) {
2N/A config.writeLog("datagram_io_error",
2N/A message);
2N/A
2N/A }
2N/A
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.NETWORK_ERROR,
2N/A "datagram_io_error",
2N/A message);
2N/A
2N/A }
2N/A }
2N/A
2N/A // If nothing, then we've timed out. DAs with no matching
2N/A // info should at least return a reply.
2N/A
2N/A if (rply == null) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.NETWORK_TIMED_OUT,
2N/A "udp_timeout",
2N/A new Object[] {addr});
2N/A
2N/A }
2N/A
2N/A return rply;
2N/A }
2N/A
2N/A /**
2N/A * Transact a message using TCP, since the reply was too big.
2N/A * @parameter addr Address of the DA to contact.
2N/A * @parameter msg The message object to use.
2N/A * @parameter cacheIt Cache socket, if new.
2N/A * @return The SrvLocMsg returned if any.
2N/A * @exception ServiceLocationException
2N/A * If results cannot be obtained in the timeout interval
2N/A * specified in the 'config.'
2N/A * If networking resources cannot be obtained or used
2N/A * effectively.
2N/A */
2N/A
2N/A static SrvLocMsg
2N/A transactTCPMsg(InetAddress addr, SrvLocMsg msg, boolean cacheIt)
2N/A throws ServiceLocationException {
2N/A
2N/A // Get the config object if we need it.
2N/A
2N/A if (config == null) {
2N/A config = SLPConfig.getSLPConfig();
2N/A
2N/A }
2N/A
2N/A SrvLocMsg rply = null;
2N/A
2N/A try {
2N/A
2N/A // Transact the message, taking care of socket caching.
2N/A
2N/A rply = transactMsg(addr, msg, cacheIt, true);
2N/A
2N/A } catch (InterruptedIOException ex) {
2N/A Object[] message = {addr};
2N/A
2N/A if (config.traceDrop()|| config.traceDATraffic()) {
2N/A config.writeLog("tcp_timeout",
2N/A message);
2N/A
2N/A }
2N/A
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.NETWORK_TIMED_OUT,
2N/A "tcp_timeout",
2N/A message);
2N/A
2N/A } catch (IOException ex) {
2N/A Object[] message = {addr, ex.getMessage()};
2N/A
2N/A if (config.traceDrop() || config.traceDATraffic()) {
2N/A config.writeLog("tcp_io_error",
2N/A message);
2N/A
2N/A }
2N/A
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.NETWORK_ERROR,
2N/A "tcp_io_error",
2N/A message);
2N/A
2N/A }
2N/A
2N/A // Filter reply for nulls, invalid xid.
2N/A
2N/A if (!filterRply(msg, rply, addr)) {
2N/A return null;
2N/A
2N/A }
2N/A
2N/A return rply;
2N/A }
2N/A
2N/A // Uncache a socket.
2N/A
2N/A static private void uncacheSocket(InetAddress addr, Socket s) {
2N/A
2N/A try {
2N/A
2N/A s.close();
2N/A
2N/A } catch (IOException ex) {
2N/A
2N/A }
2N/A
2N/A TCPSocketCache.remove(addr);
2N/A
2N/A }
2N/A
2N/A // Get a (possibly cached) TCP socket, cache it if cache is on.
2N/A
2N/A static private Socket getTCPSocket(InetAddress addr, boolean cacheIt)
2N/A throws IOException {
2N/A
2N/A Socket s = null;
2N/A
2N/A // We use the cached socket if we've got it.
2N/A
2N/A s = (Socket)TCPSocketCache.get(addr);
2N/A
2N/A if (s == null) {
2N/A s = new Socket(addr, Defaults.iSLPPort);
2N/A
2N/A // Set it so the socket will block for fixed timeout.
2N/A
2N/A s.setSoTimeout(config.getTCPTimeout());
2N/A
2N/A }
2N/A
2N/A // We cache it if we're supposed to.
2N/A
2N/A if (cacheIt) {
2N/A TCPSocketCache.put(addr, s);
2N/A
2N/A }
2N/A
2N/A return s;
2N/A }
2N/A
2N/A // Transact the message, using cached socket if necessary. Retry if
2N/A // flag is true.
2N/A
2N/A static private SrvLocMsg
2N/A transactMsg(InetAddress addr,
2N/A SrvLocMsg msg,
2N/A boolean cacheIt,
2N/A boolean retry)
2N/A throws InterruptedIOException, IOException, ServiceLocationException {
2N/A
2N/A Socket s = null;
2N/A byte outbuf[] = getBytes(msg, false, true);
2N/A
2N/A try {
2N/A
2N/A s = getTCPSocket(addr, cacheIt);
2N/A
2N/A DataOutputStream dos = new DataOutputStream(s.getOutputStream());
2N/A DataInputStream dis = new DataInputStream(s.getInputStream());
2N/A
2N/A // In case the server cuts us off...
2N/A
2N/A try {
2N/A
2N/A // Only one thread at a time gets to use this socket, in case
2N/A // it was cached. Otherwise, we *may* get interleaved i/o.
2N/A
2N/A synchronized (s) {
2N/A
2N/A // Send the request.
2N/A
2N/A dos.write(outbuf, 0, outbuf.length);
2N/A
2N/A // Read reply.
2N/A
2N/A return internalize(dis, addr);
2N/A
2N/A }
2N/A
2N/A } catch (IOException ex) {
2N/A
2N/A // Uncache it, get a new one. If that one doesn't work, we're
2N/A // hosed.
2N/A
2N/A uncacheSocket(addr, s);
2N/A
2N/A s = null;
2N/A
2N/A if (!retry) {
2N/A throw ex;
2N/A
2N/A }
2N/A
2N/A // Recursively call ourselves to take care of this, but
2N/A // don't retry it.
2N/A
2N/A return transactMsg(addr, msg, cacheIt, false);
2N/A
2N/A }
2N/A
2N/A } finally {
2N/A
2N/A if (s != null && !cacheIt) {
2N/A uncacheSocket(addr, s);
2N/A
2N/A }
2N/A }
2N/A }
2N/A
2N/A // Externalize the message into bytes.
2N/A
2N/A static protected byte[] getBytes(SrvLocMsg slm,
2N/A boolean isMulti,
2N/A boolean isTCP)
2N/A throws ServiceLocationException {
2N/A
2N/A ByteArrayOutputStream baos = new ByteArrayOutputStream();
2N/A SrvLocHeader hdr = slm.getHeader();
2N/A
2N/A hdr.externalize(baos, isMulti, isTCP);
2N/A
2N/A byte[] outbuf = baos.toByteArray();
2N/A
2N/A // Check if it excceds the output buffer length.
2N/A
2N/A if (hdr.overflow) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.BUFFER_OVERFLOW,
2N/A "buffer_overflow",
2N/A new Object[] {
2N/A new Integer(outbuf.length),
2N/A new Integer(config.getMTU())});
2N/A }
2N/A
2N/A return outbuf;
2N/A }
2N/A
2N/A // Filter the reply to make sure the xid matches and that it's not null.
2N/A
2N/A static protected boolean
2N/A filterRply(SrvLocMsg msg, SrvLocMsg rply, InetAddress addr) {
2N/A
2N/A SrvLocHeader mhdr = msg.getHeader();
2N/A SrvLocHeader rhdr = rply.getHeader();
2N/A
2N/A if (rply == null) {
2N/A if (config.traceDrop()) {
2N/A config.writeLog("reply_unparsable",
2N/A new Object[] {addr});
2N/A
2N/A }
2N/A
2N/A return false;
2N/A
2N/A }
2N/A
2N/A // Check for invalid xid.
2N/A
2N/A if (mhdr.xid != rhdr.xid) {
2N/A if (config.traceDrop()) {
2N/A config.writeLog("wrong_xid",
2N/A new Object[] {addr});
2N/A
2N/A }
2N/A return false;
2N/A
2N/A }
2N/A return true;
2N/A
2N/A }
2N/A
2N/A /**
2N/A * Internalize the byte array in the input stream into a SrvLocMsg
2N/A * subclass. It will be an appropriate subclass for the client agent.
2N/A * If an exception comes out of this method, it is converted into
2N/A * a SrvLocMsg with error code.
2N/A *
2N/A *
2N/A * @param dis The input stream containing the packet.
2N/A * @param addr The address of the replying agent (for error reporting).
2N/A * @return The right SrvLocMsg subclass appropriate for the Client Agent.
2N/A * If null is returned, the function code wasn't recognized,
2N/A * and so it may be appropriate for another agent.
2N/A * @exception ServiceLocationException If the character set was not valid
2N/A * or an error occured during parsing.
2N/A * @exception IOException If DataInputStream throws it.
2N/A */
2N/A
2N/A static protected SrvLocMsg internalize(DataInputStream dis,
2N/A InetAddress addr)
2N/A throws ServiceLocationException {
2N/A
2N/A int ver = 0, fun = 0;
2N/A SrvLocMsg msg = null;
2N/A SrvLocHeader hdr = null;
2N/A byte[] b = new byte[2];
2N/A
2N/A try {
2N/A
2N/A dis.readFully(b, 0, 2);
2N/A
2N/A ver = (int) ((char)b[0] & 0XFF);
2N/A fun = (int) ((char)b[1] & 0XFF);
2N/A
2N/A // Unrecognized version number if header not returned.
2N/A
2N/A if (ver != Defaults.version) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.VERSION_NOT_SUPPORTED,
2N/A "version_number_error",
2N/A new Object[] {new Integer(ver)});
2N/A
2N/A }
2N/A
2N/A // Create the header. Note that we only need to create a
2N/A // client side header here, because that is all that
2N/A // will be expected by the client side code. Note that we
2N/A // *can't* use the SrvLocHeader.newInstance() mechanism
2N/A // because Transact lives in the server as well, and
2N/A // SrvLocHeader can only handle one header class per
2N/A // version.
2N/A
2N/A hdr = new SLPHeaderV2();
2N/A
2N/A // Parse header.
2N/A
2N/A hdr.parseHeader(fun, dis);
2N/A
2N/A // Parse body.
2N/A
2N/A if ((msg = hdr.parseMsg(dis)) != null) {
2N/A
2N/A // Parse options, if any.
2N/A
2N/A hdr.parseOptions(dis);
2N/A
2N/A }
2N/A
2N/A } catch (IllegalArgumentException ex) {
2N/A
2N/A // During parsing, this can be thrown if syntax errors occur.
2N/A
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "passthrough_addr",
2N/A new Object[] {ex.getMessage(), addr});
2N/A
2N/A } catch (IOException ex) {
2N/A
2N/A // If version code is zero, then we suspect a network error,
2N/A // otherwise, it is probably a parse error.
2N/A
2N/A String fcode = (fun == 0 ? "???":Integer.toString(fun));
2N/A short exCode =
2N/A (ver == 0 ? ServiceLocationException.NETWORK_ERROR:
2N/A ServiceLocationException.PARSE_ERROR);
2N/A
2N/A // During parsing, this can be thrown if the message stream
2N/A // is improperly formatted.
2N/A
2N/A throw
2N/A new ServiceLocationException(exCode,
2N/A "ioexception_parsing",
2N/A new Object[] {
2N/A ex, fcode, addr, ex.getMessage()});
2N/A
2N/A } catch (ServiceLocationException ex) {
2N/A
2N/A // Add the address of the replying agent.
2N/A
2N/A throw
2N/A new ServiceLocationException(ex.getErrorCode(),
2N/A "passthrough_addr",
2N/A new Object[] {
2N/A ex.getMessage(), addr});
2N/A
2N/A }
2N/A
2N/A return msg;
2N/A }
2N/A
2N/A // Send out the message.
2N/A
2N/A static protected void
2N/A send(DatagramSocket ds, SrvLocMsg msg, InetAddress addr)
2N/A throws ServiceLocationException, IOException {
2N/A
2N/A byte[] outbuf = getBytes(msg, true, false);
2N/A DatagramPacket dpsend =
2N/A new DatagramPacket(outbuf, outbuf.length, addr, Defaults.iSLPPort);
2N/A ds.send(dpsend);
2N/A
2N/A }
2N/A
2N/A // Check the response and add the previous responder if it is OK.
2N/A
2N/A static protected boolean
2N/A addPreviousResponder(SrvLocMsg msg, InetAddress addr) {
2N/A
2N/A // Add incoming result to the vector.
2N/A
2N/A SrvLocHeader hdr = msg.getHeader();
2N/A Vector v = hdr.previousResponders;
2N/A String srcAddr = addr.getHostAddress();
2N/A
2N/A if (v.contains(srcAddr)) { // the SRC ignored its PR list
2N/A if (config.traceDrop()) {
2N/A config.writeLog("drop_pr",
2N/A new Object[] {
2N/A srcAddr,
2N/A Integer.toHexString(hdr.xid)});
2N/A
2N/A }
2N/A return false;
2N/A
2N/A } else {
2N/A hdr.addPreviousResponder(addr);
2N/A return true;
2N/A
2N/A }
2N/A }
2N/A
2N/A // Transact an active request for DA or SA adverts.
2N/A
2N/A static Vector transactActiveAdvertRequest(ServiceType type,
2N/A SrvLocMsg rqst,
2N/A ServerDATable daTable)
2N/A throws ServiceLocationException {
2N/A
2N/A // Perform active advertisement.
2N/A
2N/A Vector ret = new Vector();
2N/A Vector results = new Vector();
2N/A
2N/A // Create Transact object and start.
2N/A
2N/A Transact tran = new Transact(rqst,
2N/A results,
2N/A config.getMulticastTimeouts(),
2N/A Integer.MAX_VALUE, // config doesn't apply
2N/A config.getMulticastAddress(),
2N/A true);
2N/A
2N/A Thread multiThread = new Thread(tran);
2N/A
2N/A multiThread.start();
2N/A
2N/A // Wait until the TransactConverge thread is done, if necessary.
2N/A
2N/A try {
2N/A multiThread.join();
2N/A
2N/A } catch (InterruptedException ex) {
2N/A
2N/A }
2N/A
2N/A ServiceLocationException ex = tran.exErr;
2N/A
2N/A // Report error.
2N/A
2N/A if (ex != null && config.traceDATraffic()) {
2N/A config.writeLog("sdat_active_err",
2N/A new Object[] {new Integer(ex.getErrorCode()),
2N/A ex.getMessage()});
2N/A
2N/A throw ex;
2N/A
2N/A }
2N/A
2N/A // Process the results.
2N/A
2N/A int i, n = results.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A Object msg = results.elementAt(i);
2N/A
2N/A if ((type.equals(Defaults.DA_SERVICE_TYPE) &&
2N/A !(msg instanceof CDAAdvert)) ||
2N/A (type.equals(Defaults.SA_SERVICE_TYPE) &&
2N/A !(msg instanceof CSAAdvert))) {
2N/A
2N/A if (config.traceDrop()) {
2N/A config.writeLog("sdat_nonadvert_err",
2N/A new Object[] {
2N/A msg});
2N/A
2N/A }
2N/A
2N/A continue;
2N/A }
2N/A
2N/A // Let DA table handle it if it`s a DAAdvert.
2N/A
2N/A if (type.equals(Defaults.DA_SERVICE_TYPE)) {
2N/A CDAAdvert advert = (CDAAdvert)msg;
2N/A
2N/A daTable.handleAdvertIn(advert);
2N/A
2N/A } else {
2N/A
2N/A // Add scopes from the SAAdvert if not already there.
2N/A
2N/A SrvLocHeader hdr = ((SrvLocMsg)msg).getHeader();
2N/A
2N/A int j, m = hdr.scopes.size();
2N/A
2N/A for (j = 0; j < m; j++) {
2N/A Object o = hdr.scopes.elementAt(j);
2N/A
2N/A if (!ret.contains(o)) {
2N/A ret.addElement(o);
2N/A
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A return ret;
2N/A }
2N/A
2N/A // Construct a Transact object to run a convergence transaction in
2N/A // a separate thread.
2N/A
2N/A Transact(SrvLocMsg msg,
2N/A Vector ret,
2N/A int[] msT,
2N/A int mResults,
2N/A InetAddress address,
2N/A boolean continueAfterFound) {
2N/A
2N/A msgOut = msg;
2N/A returns = ret;
2N/A MSTimeouts = msT;
2N/A maxResults = mResults;
2N/A this.address = address;
2N/A this.continueAfterFound = continueAfterFound;
2N/A }
2N/A
2N/A // Run the multicast convergence algorithm.
2N/A
2N/A public void run() {
2N/A
2N/A Exception xes = null;
2N/A DatagramSocket ds = null;
2N/A
2N/A // Get the config object if we need it.
2N/A
2N/A if (config == null) {
2N/A config = SLPConfig.getSLPConfig();
2N/A
2N/A }
2N/A
2N/A // Set thread name.
2N/A
2N/A if (config.isBroadcastOnly()) {
2N/A Thread.currentThread().setName("SLP Broadcast Transact");
2N/A address = config.getBroadcastAddress();
2N/A
2N/A } else {
2N/A Thread.currentThread().setName("SLP Multicast Transact");
2N/A
2N/A }
2N/A
2N/A try {
2N/A
2N/A // Multicast out on the default interface only.
2N/A
2N/A ds = config.getMulticastSocketOnInterface(config.getLocalHost(),
2N/A true);
2N/A
2N/A // Perform convergence.
2N/A
2N/A transactConvergeMsg(address,
2N/A ds,
2N/A msgOut,
2N/A returns,
2N/A MSTimeouts,
2N/A maxResults,
2N/A continueAfterFound);
2N/A
2N/A ds.close();
2N/A
2N/A ds = null;
2N/A
2N/A } catch (ServiceLocationException ex) {
2N/A
2N/A // Ignore DA_BUSY, the DA may free up later.
2N/A
2N/A if (ex.getErrorCode() != ServiceLocationException.DA_BUSY) {
2N/A exErr = ex;
2N/A xes = ex;
2N/A
2N/A }
2N/A
2N/A } catch (Exception ex) {
2N/A
2N/A // Create new exception to be thrown.
2N/A
2N/A xes = ex;
2N/A exErr = new ServiceLocationException(
2N/A ServiceLocationException.INTERNAL_SYSTEM_ERROR,
2N/A "passthrough",
2N/A new Object[] {ex.getMessage()});
2N/A
2N/A } finally {
2N/A
2N/A // Close the socket if it's been opened.
2N/A
2N/A if (ds != null) {
2N/A ds.close();
2N/A
2N/A }
2N/A }
2N/A
2N/A // Log any errors.
2N/A
2N/A if (xes != null) {
2N/A StringWriter sw = new StringWriter();
2N/A PrintWriter pw = new PrintWriter(sw);
2N/A
2N/A xes.printStackTrace(pw);
2N/A pw.flush();
2N/A
2N/A config.writeLog("multicast_error",
2N/A new Object[] {xes.getMessage(),
2N/A sw.toString()});
2N/A
2N/A }
2N/A }
2N/A
2N/A /**
2N/A * Send the message using multicast and use convergence to gather the
2N/A * results. Note that this routine must be synchronized because
2N/A * only one multicast can be active at a time; othewise, the client
2N/A * may get back an unexpected result. However, there can be many unicast
2N/A * requests active along with a multicast request, hence the separate
2N/A * thread for multicast.
2N/A *
2N/A * The subtlety of the timing routine is that it will only resend the
2N/A * message when one of the multicast convergence timeout intervals
2N/A * elapses. Further, for efficiency, it will give up after a complete
2N/A * interval has gone by without receiving any results. This may mean
2N/A * that the intervals have to be extended in larger networks. In the
2N/A * common case, multicast convergence will complete under 3 seconds
2N/A * as all results will arrive during the first interval (1 second long)
2N/A * and none will arrive during the second interval.
2N/A *
2N/A * @param addr The multicast/broadcast address to send the request to.
2N/A * @param ds The datagram socket to send on.
2N/A * @param msg The message to send.
2N/A * @param vResult A vector in which to put the returns.
2N/A * @param msTimeouts Array of timeout values for multicast convergence.
2N/A * @param maxResults Maximum replies desired.
2N/A * @param continueAfterFound If true, continue after something is
2N/A * found. Try three times if nothing was
2N/A * found. If false, exit at the first
2N/A * timeout. DA discovery should set this
2N/A * to true so as many DAs as possible are
2N/A * found, otherwise, it should be false.
2N/A * @exception ServiceLocationException
2N/A * If results cannot be obtained in the timeout interval
2N/A * specified in the 'config.' or
2N/A * if networking resources cannot be obtained or used
2N/A * effectively.
2N/A */
2N/A
2N/A static public void
2N/A transactConvergeMsg(InetAddress addr,
2N/A DatagramSocket ds,
2N/A SrvLocMsg msg,
2N/A Vector vResult,
2N/A int[] msTimeouts,
2N/A int maxResults,
2N/A boolean continueAfterFound)
2N/A throws ServiceLocationException {
2N/A
2N/A // Get the config object if we need it.
2N/A
2N/A if (config == null) {
2N/A config = SLPConfig.getSLPConfig();
2N/A
2N/A }
2N/A
2N/A int numReplies = 0;
2N/A int tries = 0;
2N/A SrvLocMsg rply = null;
2N/A ByteArrayOutputStream baos = null;
2N/A int multiMax = config.getMulticastMaximumWait();
2N/A long lStartTime = System.currentTimeMillis();
2N/A int mtu = config.getMTU();
2N/A
2N/A try {
2N/A
2N/A // Send the request for the 1st iteration. It will be sent again
2N/A // only when the timeout intervals elapse.
2N/A
2N/A send(ds, msg, addr);
2N/A tries++;
2N/A
2N/A long lTimeSent = System.currentTimeMillis();
2N/A
2N/A // Continue collecting results only as long as we need more for
2N/A // the 'max results' configuration.
2N/A
2N/A while (numReplies < maxResults) {
2N/A
2N/A // Set up the reply buffer.
2N/A
2N/A byte [] incoming = new byte[mtu];
2N/A DatagramPacket dprecv =
2N/A new DatagramPacket(incoming, incoming.length);
2N/A
2N/A // Block on receive (no longer than max timeout - time spent).
2N/A
2N/A int iTimeout =
2N/A getTimeout(lStartTime, lTimeSent, multiMax, msTimeouts);
2N/A
2N/A if (iTimeout < 0) {
2N/A break; // we have no time left!
2N/A }
2N/A
2N/A ds.setSoTimeout(iTimeout);
2N/A
2N/A try {
2N/A ds.receive(dprecv);
2N/A
2N/A } catch (InterruptedIOException ex) {
2N/A
2N/A // We try sending at least three times, unless there was
2N/A // a timeout. If continueAfterFound is false, we exit
2N/A // after the first timeout if something was found.
2N/A
2N/A if ((!continueAfterFound && numReplies > 0) ||
2N/A (int)(System.currentTimeMillis() - lStartTime) > multiMax ||
2N/A tries >= 3) {
2N/A break;
2N/A
2N/A }
2N/A
2N/A // Now resend the request...
2N/A
2N/A send(ds, msg, addr);
2N/A tries++;
2N/A
2N/A lTimeSent = System.currentTimeMillis();
2N/A continue; // since we did not receive anything, continue...
2N/A
2N/A }
2N/A
2N/A // Data was received without timeout or fail.
2N/A
2N/A DataInputStream dis =
2N/A new DataInputStream(
2N/A new ByteArrayInputStream(dprecv.getData()));
2N/A
2N/A InetAddress raddr = dprecv.getAddress();
2N/A rply = internalize(dis, raddr);
2N/A
2N/A if (!filterRply(msg, rply, raddr)) {
2N/A continue;
2N/A
2N/A }
2N/A
2N/A // Add this responder to previous responders. If the message
2N/A // was already received but the SA resent because it isn't
2N/A // doing multicast convergence correctly, then ignore it.
2N/A
2N/A if (!addPreviousResponder(msg, raddr)) {
2N/A continue;
2N/A
2N/A }
2N/A
2N/A // Handle any overflow thru TCP.
2N/A
2N/A SrvLocHeader rhdr = rply.getHeader();
2N/A
2N/A if (rhdr.overflow) {
2N/A
2N/A rply = transactTCPMsg(raddr, msg, false);
2N/A
2N/A if (rply == null) {
2N/A continue;
2N/A
2N/A }
2N/A
2N/A rhdr = rply.getHeader();
2N/A }
2N/A
2N/A // Add response to list.
2N/A
2N/A if (vResult.size() < maxResults) {
2N/A vResult.addElement(rply);
2N/A
2N/A }
2N/A
2N/A // Increment the number of results returned.
2N/A
2N/A numReplies += rhdr.iNumReplies;
2N/A
2N/A // Exit if we should not continue.
2N/A
2N/A if (!continueAfterFound) {
2N/A break;
2N/A
2N/A }
2N/A }
2N/A } catch (ServiceLocationException ex) {
2N/A
2N/A // If we broke off because the previous responder's list is too
2N/A // long, then return, otherwise throw the exception again.
2N/A
2N/A if (ex.getErrorCode() ==
2N/A ServiceLocationException.PREVIOUS_RESPONDER_OVERFLOW) {
2N/A return;
2N/A
2N/A }
2N/A
2N/A throw ex;
2N/A
2N/A } catch (IOException ex) {
2N/A
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.NETWORK_ERROR,
2N/A "ioexception_conv",
2N/A new Object[] {ex, ex.getMessage()});
2N/A
2N/A }
2N/A }
2N/A
2N/A // Calculate the multicast timeout depending on where we are in the loop.
2N/A
2N/A static private int
2N/A getTimeout(long lStart, long lSent, int iTimeout, int[] a_iTOs) {
2N/A int iTotal = (int)(lSent - lStart);
2N/A
2N/A if (iTimeout < iTotal) {
2N/A return -1;
2N/A
2N/A }
2N/A
2N/A int iWaitTotal = 0;
2N/A int i;
2N/A
2N/A for (i = 0; i < a_iTOs.length; i++) {
2N/A iWaitTotal += a_iTOs[i];
2N/A
2N/A int iTillNext = (iWaitTotal - iTotal);
2N/A
2N/A if (iTotal < iWaitTotal) {
2N/A if (iTimeout < (iTotal + iTillNext)) {
2N/A return (iTimeout - iTotal); // max to wait is iTimeout
2N/A
2N/A } else {
2N/A return iTillNext; // otherwise wait till next interval
2N/A }
2N/A }
2N/A }
2N/A
2N/A return -1; // if we get here we have waited past all of the timeouts
2N/A }
2N/A
2N/A static {
2N/A
2N/A config = SLPConfig.getSLPConfig();
2N/A
2N/A }
2N/A}