/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.jini.discovery;
import net.jini.core.discovery.LookupLocator;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.util.Collection;
import java.util.Iterator;
import java.util.Vector;
import java.security.AccessController;
import java.security.PrivilegedAction;
import net.jini.core.lookup.ServiceID;
/**
* Encapsulate the details of marshaling a multicast announcement into
* one or more packets.
*
* @author Sun Microsystems, Inc.
*
* @see IncomingMulticastAnnouncement
*/
public class OutgoingMulticastAnnouncement {
/**
* The minimum size we allow for an outgoing packet.
*/
protected static final int minMaxPacketSize = 512;
/**
* The maximum size we allow for an outgoing packet. This may be
* controlled using the <tt>net.jini.discovery.mtu</tt> system
* property. The value cannot be less than 512. The default is 512.
*/
protected static final int maxPacketSize =
((Integer)AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
try {
return Integer.getInteger("net.jini.discovery.mtu",
minMaxPacketSize);
} catch (SecurityException e) {
return new Integer(minMaxPacketSize);
}
}
})).intValue();
/**
* The current version of the multicast announcement protocol.
*/
protected static final int protocolVersion = 1;
/**
* Marshal a multicast announcement into one or more datagram
* packets. These packets are guaranteed to contain, between
* them, all of the groups of which the to-be-announced lookup
* service is a member. <p>
*
* The datagram packets returned will have been initialized for
* sending to the appropriate multicast address and UDP port.
*
* @param id the ServiceID we are announcing
* @param loc a LookupLocator that will allow unicast discovery of
* the lookup service we are announcing
* @param groups the groups of which the announced lookup service
* is a member
* @return an array of datagram packets, which will always contain
* at least one member
* @exception IOException a problem occurred during marshaling
*/
public static DatagramPacket[] marshal(ServiceID id,
LookupLocator loc,
String[] groups)
throws IOException
{
if (maxPacketSize < minMaxPacketSize)
throw new RuntimeException("value of net.jini.discovery.mtu property is less than " + minMaxPacketSize);
// Marshal the fixed header stuff.
byte[] marshaledHeader;
{
ByteArrayOutputStream hbs = new ByteArrayOutputStream();
DataOutputStream hos = new DataOutputStream(hbs);
// Write out the relatively fixed stuff first.
hos.writeInt(protocolVersion);
hos.writeUTF(loc.getHost());
hos.writeInt(loc.getPort());
id.writeBytes(hos);
marshaledHeader = hbs.toByteArray();
}
if (marshaledHeader.length > maxPacketSize)
throw new IllegalArgumentException("host name marshals too large");
byte[][] marshaledGroups = new byte[groups.length][];
// Length of the longest group.
int longestGroup = -1;
for (int i = 0; i < groups.length; i++) {
ByteArrayOutputStream gbs = new ByteArrayOutputStream();
DataOutputStream gos = new DataOutputStream(gbs);
gos.writeUTF(groups[i]);
gos.flush();
marshaledGroups[i] = gbs.toByteArray();
if (marshaledHeader.length +
4 /* heard count */ + /* assume no service IDs heard */
4 /* group count */ +
marshaledGroups[i].length > maxPacketSize)
throw new IllegalArgumentException("group name marshals too large (" +
marshaledGroups[i].length +
" bytes)");
if (marshaledGroups[i].length > longestGroup)
longestGroup = marshaledGroups[i].length;
}
// Build up a collection of requests.
Collection reqs = new Vector();
{
InetAddress addr = Constants.getAnnouncementAddress();
if (groups.length == 0) {
ByteArrayOutputStream bs =
new ByteArrayOutputStream(maxPacketSize);
DataOutputStream os = new DataOutputStream(bs);
os.write(marshaledHeader);
os.writeInt(0);
byte[] payload = bs.toByteArray();
reqs.add(new DatagramPacket(payload, payload.length,
addr, Constants.discoveryPort));
} else {
for (int curr = 0; curr < marshaledGroups.length; ) {
ByteArrayOutputStream bs =
new ByteArrayOutputStream(maxPacketSize);
DataOutputStream os = new DataOutputStream(bs);
os.write(marshaledHeader);
os.flush();
int bytes = bs.size() + 4 /* for group count */;
int last = curr;
while (last < marshaledGroups.length &&
bytes + marshaledGroups[last].length <= maxPacketSize)
{
bytes += marshaledGroups[last].length;
last += 1;
}
os.writeInt(last - curr);
while (curr < last) {
os.write(marshaledGroups[curr++]);
}
os.flush();
byte[] payload = bs.toByteArray();
reqs.add(new DatagramPacket(payload, payload.length,
addr, Constants.discoveryPort));
}
}
}
Iterator iter = reqs.iterator();
DatagramPacket[] ary = new DatagramPacket[reqs.size()];
for (int i = 0; iter.hasNext(); i++) {
ary[i] = (DatagramPacket) iter.next();
}
return ary;
}
}