/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* ident "%Z%%M% %I% %E% SMI"
*
* Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
*/
// Transact.java: Low level details of performing an SLP
// network transaction.
/**
* Transact performs the low level details for transacting an SLP network
* query. Note that, in the future, this class may spin separate threads
* for DA requests as well.
*/
// Cache of open TCP sockets.
// SLP config object.
// Message to send.
// Vector of return values.
// Timeout for multicast convergence. Varies if it's DA discovery or
// request multicast.
protected int[] MSTimeouts;
// Maximum results desired for multicast.
// Exception to throw.
// Multicast address to use.
// If this is true, continue multicast after the first set of stuff
// is found. Exit when three tries have happened without finding
// anything.
boolean continueAfterFound = false;
/**
* Perform a query to the SLP network. The multicast query is performed
* in a separate thread for performance reasons. DAs having the
* same scope set are queried until one answers. These DAs form
* an equivalence class.
*
* @param daEquivClasses Vector of DATable.DARecord objects in the
* same equivalence clase w.r.t. scopes.
* @param uniMsg A unicast message to send.
* @param multiMsg A multicast message to send.
* @param address Multicast address to use.
* @return Vector of SrvLocMsg objects with results.
*/
static Vector
throws ServiceLocationException {
// If we need to multicast, then start the multicast thread.
// Create a new Transact multicast thread.
// The final argument to the constructor of Transact determines
// whether to return after the first result or to continue to
// gather more than one result. The value to this field
// continueAfterFound MUST be set to 'true' or else multicast
// based discovery will find the first result, not all results,
// as it should.
tracon =
ret,
true); // continueAfterFound
// Run it.
multiThread.start();
}
// Go through the msgTable doing all the DAs.
if (daEquivClasses != null) {
exx =
ret,
}
// Wait until the TransactConverge thread is done, if necessary.
if (multiThread != null) {
try {
multiThread.join();
} catch (InterruptedException ex) {
}
}
// If there was a problem in either the multicast thread or in
// the unicast call, throw an exception, but *only* if no
// results came back.
throw exx;
}
}
}
}
}
// Return the result to the client.
return ret;
}
/**
* Transact a message with DAs. Put the returned SrvLocMsg
* object into the Vector ret.
*
* @param daEquivClasses Vector of DATable.DARecord objects in the
* same equivalence clase w.r.t. scopes.
* @param msg SrvLocMsg Message to send.
* @param ret Vector for returns.
* @param maxResults Maximum results expected.
* @return A ServiceLocationException object if an exception occured.
* @exception ServiceLocationException
* If results cannot be obtained in the timeout interval
* specified in the 'config.' or
* If networking resources cannot be obtained or used
* effectively.
*/
static ServiceLocationException
int maxResults) {
// Get the config object if we need it.
}
int i, n = daEquivClasses.size();
int numReplies = 0;
try {
// Go through the DA address equivalence classes we need
// to query.
// Get a new outgoing socket.
ds = new DatagramSocket();
}
// Go through the DA addresses until we get a reply from one.
while (en.hasMoreElements()) {
try {
if (config.traceDATraffic()) {
new Object[] {
addr});
}
// Get the reply message if any.
continue;
}
if (config.traceDATraffic()) {
new Object[] {
addr});
}
// If overflow, try TCP.
if (config.traceDATraffic()) {
new Object[] {
addr});
}
if (config.traceDATraffic()) {
new Object[] {
addr});
}
continue;
}
}
// Increment number of replies we received.
// Add to return vector.
// Break out of the loop, since we only need one in
// this equivalence class.
break;
} catch (ServiceLocationException ex) {
new Object[] {
addr,
ex.getMessage()});
// In case we are querying more than one DA, we
// save th exception, returning it to the caller to
// decide if it should be thrown. We ignore DA_BUSY,
// though, since the DA may free up later.
}
// If the error code is NETWORK_TIMED_OUT, then remove
// this DA from the DA table. If it's just down
// temporarily, we'll get it next time we go to
// the server to get the DA addresses.
if (errCode ==
if (config.traceDATraffic()) {
new Object[] {
}
}
}
}
}
} catch (SocketException ex) {
exx =
"socket_creation_failure",
} finally {
// Clean up socket.
}
}
return exx;
}
/**
* Transact a message via. UDP. Try a maximum of three times if
* a timeout.
*
* @param ds The datagram socket to use.
* @param addr The DA to contact.
* @param msg The message to send.
* @return The SrvLocMsg returned or null if none.
* @exception ServiceLocationException Due to errors in parsing message.
*/
static private SrvLocMsg
throws ServiceLocationException {
// Construct the datagram packet to send.
// Resend for number of timeouts in timeout interval.
int i;
// Catch timeout and IO errors.
try {
// Process result into a reply object.
new DataInputStream(
break;
} catch (InterruptedIOException ex) {
// Did not get it on the first timeout, try again.
}
continue;
} catch (IOException ex) {
message);
}
throw
"datagram_io_error",
message);
}
}
// If nothing, then we've timed out. DAs with no matching
// info should at least return a reply.
throw
"udp_timeout",
}
return rply;
}
/**
* Transact a message using TCP, since the reply was too big.
* @parameter addr Address of the DA to contact.
* @parameter msg The message object to use.
* @parameter cacheIt Cache socket, if new.
* @return The SrvLocMsg returned if any.
* @exception ServiceLocationException
* If results cannot be obtained in the timeout interval
* specified in the 'config.'
* If networking resources cannot be obtained or used
* effectively.
*/
static SrvLocMsg
throws ServiceLocationException {
// Get the config object if we need it.
}
try {
// Transact the message, taking care of socket caching.
} catch (InterruptedIOException ex) {
message);
}
throw
"tcp_timeout",
message);
} catch (IOException ex) {
message);
}
throw
"tcp_io_error",
message);
}
// Filter reply for nulls, invalid xid.
return null;
}
return rply;
}
// Uncache a socket.
try {
s.close();
} catch (IOException ex) {
}
}
// Get a (possibly cached) TCP socket, cache it if cache is on.
throws IOException {
// We use the cached socket if we've got it.
if (s == null) {
// Set it so the socket will block for fixed timeout.
}
// We cache it if we're supposed to.
if (cacheIt) {
}
return s;
}
// Transact the message, using cached socket if necessary. Retry if
// flag is true.
static private SrvLocMsg
boolean cacheIt,
boolean retry)
try {
// In case the server cuts us off...
try {
// Only one thread at a time gets to use this socket, in case
// it was cached. Otherwise, we *may* get interleaved i/o.
synchronized (s) {
// Send the request.
// Read reply.
}
} catch (IOException ex) {
// Uncache it, get a new one. If that one doesn't work, we're
// hosed.
uncacheSocket(addr, s);
s = null;
if (!retry) {
throw ex;
}
// Recursively call ourselves to take care of this, but
// don't retry it.
}
} finally {
uncacheSocket(addr, s);
}
}
}
// Externalize the message into bytes.
boolean isMulti,
boolean isTCP)
throws ServiceLocationException {
// Check if it excceds the output buffer length.
throw
"buffer_overflow",
new Object[] {
}
return outbuf;
}
// Filter the reply to make sure the xid matches and that it's not null.
static protected boolean
}
return false;
}
// Check for invalid xid.
}
return false;
}
return true;
}
/**
* Internalize the byte array in the input stream into a SrvLocMsg
* subclass. It will be an appropriate subclass for the client agent.
* If an exception comes out of this method, it is converted into
* a SrvLocMsg with error code.
*
*
* @param dis The input stream containing the packet.
* @param addr The address of the replying agent (for error reporting).
* @return The right SrvLocMsg subclass appropriate for the Client Agent.
* If null is returned, the function code wasn't recognized,
* and so it may be appropriate for another agent.
* @exception ServiceLocationException If the character set was not valid
* or an error occured during parsing.
* @exception IOException If DataInputStream throws it.
*/
throws ServiceLocationException {
byte[] b = new byte[2];
try {
// Unrecognized version number if header not returned.
throw
"version_number_error",
}
// Create the header. Note that we only need to create a
// client side header here, because that is all that
// will be expected by the client side code. Note that we
// *can't* use the SrvLocHeader.newInstance() mechanism
// because Transact lives in the server as well, and
// SrvLocHeader can only handle one header class per
// version.
hdr = new SLPHeaderV2();
// Parse header.
// Parse body.
// Parse options, if any.
}
} catch (IllegalArgumentException ex) {
// During parsing, this can be thrown if syntax errors occur.
throw
"passthrough_addr",
} catch (IOException ex) {
// If version code is zero, then we suspect a network error,
// otherwise, it is probably a parse error.
short exCode =
// During parsing, this can be thrown if the message stream
// is improperly formatted.
throw
"ioexception_parsing",
new Object[] {
} catch (ServiceLocationException ex) {
// Add the address of the replying agent.
throw
"passthrough_addr",
new Object[] {
}
return msg;
}
// Send out the message.
static protected void
throws ServiceLocationException, IOException {
}
// Check the response and add the previous responder if it is OK.
static protected boolean
// Add incoming result to the vector.
new Object[] {
}
return false;
} else {
return true;
}
}
// Transact an active request for DA or SA adverts.
throws ServiceLocationException {
// Perform active advertisement.
// Create Transact object and start.
true);
multiThread.start();
// Wait until the TransactConverge thread is done, if necessary.
try {
multiThread.join();
} catch (InterruptedException ex) {
}
// Report error.
ex.getMessage()});
throw ex;
}
// Process the results.
for (i = 0; i < n; i++) {
new Object[] {
msg});
}
continue;
}
// Let DA table handle it if it`s a DAAdvert.
} else {
// Add scopes from the SAAdvert if not already there.
for (j = 0; j < m; j++) {
ret.addElement(o);
}
}
}
}
return ret;
}
// Construct a Transact object to run a convergence transaction in
// a separate thread.
int[] msT,
int mResults,
boolean continueAfterFound) {
MSTimeouts = msT;
this.continueAfterFound = continueAfterFound;
}
// Run the multicast convergence algorithm.
public void run() {
// Get the config object if we need it.
}
// Set thread name.
if (config.isBroadcastOnly()) {
} else {
}
try {
// Multicast out on the default interface only.
true);
// Perform convergence.
ds,
} catch (ServiceLocationException ex) {
// Ignore DA_BUSY, the DA may free up later.
}
// Create new exception to be thrown.
exErr = new ServiceLocationException(
"passthrough",
} finally {
// Close the socket if it's been opened.
}
}
// Log any errors.
}
}
/**
* Send the message using multicast and use convergence to gather the
* results. Note that this routine must be synchronized because
* only one multicast can be active at a time; othewise, the client
* may get back an unexpected result. However, there can be many unicast
* requests active along with a multicast request, hence the separate
* thread for multicast.
*
* The subtlety of the timing routine is that it will only resend the
* message when one of the multicast convergence timeout intervals
* elapses. Further, for efficiency, it will give up after a complete
* interval has gone by without receiving any results. This may mean
* that the intervals have to be extended in larger networks. In the
* common case, multicast convergence will complete under 3 seconds
* as all results will arrive during the first interval (1 second long)
* and none will arrive during the second interval.
*
* @param ds The datagram socket to send on.
* @param msg The message to send.
* @param vResult A vector in which to put the returns.
* @param msTimeouts Array of timeout values for multicast convergence.
* @param maxResults Maximum replies desired.
* @param continueAfterFound If true, continue after something is
* found. Try three times if nothing was
* found. If false, exit at the first
* timeout. DA discovery should set this
* to true so as many DAs as possible are
* found, otherwise, it should be false.
* @exception ServiceLocationException
* If results cannot be obtained in the timeout interval
* specified in the 'config.' or
* if networking resources cannot be obtained or used
* effectively.
*/
static public void
int[] msTimeouts,
int maxResults,
boolean continueAfterFound)
throws ServiceLocationException {
// Get the config object if we need it.
}
int numReplies = 0;
int tries = 0;
try {
// Send the request for the 1st iteration. It will be sent again
// only when the timeout intervals elapse.
tries++;
// Continue collecting results only as long as we need more for
// the 'max results' configuration.
while (numReplies < maxResults) {
// Set up the reply buffer.
// Block on receive (no longer than max timeout - time spent).
int iTimeout =
if (iTimeout < 0) {
break; // we have no time left!
}
try {
} catch (InterruptedIOException ex) {
// We try sending at least three times, unless there was
// a timeout. If continueAfterFound is false, we exit
// after the first timeout if something was found.
tries >= 3) {
break;
}
// Now resend the request...
tries++;
continue; // since we did not receive anything, continue...
}
// Data was received without timeout or fail.
new DataInputStream(
continue;
}
// Add this responder to previous responders. If the message
// was already received but the SA resent because it isn't
// doing multicast convergence correctly, then ignore it.
continue;
}
// Handle any overflow thru TCP.
continue;
}
}
// Add response to list.
}
// Increment the number of results returned.
// Exit if we should not continue.
if (!continueAfterFound) {
break;
}
}
} catch (ServiceLocationException ex) {
// If we broke off because the previous responder's list is too
// long, then return, otherwise throw the exception again.
if (ex.getErrorCode() ==
return;
}
throw ex;
} catch (IOException ex) {
throw
"ioexception_conv",
}
}
// Calculate the multicast timeout depending on where we are in the loop.
static private int
return -1;
}
int iWaitTotal = 0;
int i;
iWaitTotal += a_iTOs[i];
if (iTotal < iWaitTotal) {
} else {
return iTillNext; // otherwise wait till next interval
}
}
}
return -1; // if we get here we have waited past all of the timeouts
}
static {
}
}