2 * Copyright (c) 2001-2007 Sun Microsystems, Inc. All rights reserved.
4 * The Sun Project JXTA(TM) Software License
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
16 * 3. The end-user documentation included with the redistribution, if any, must
17 * include the following acknowledgment: "This product includes software
18 * developed by Sun Microsystems, Inc. for JXTA(TM) technology."
19 * Alternately, this acknowledgment may appear in the software itself, if
20 * and wherever such third-party acknowledgments normally appear.
22 * 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must
23 * not be used to endorse or promote products derived from this software
24 * without prior written permission. For written permission, please contact
25 * Project JXTA at http://www.jxta.org.
27 * 5. Products derived from this software may not be called "JXTA", nor may
28 * "JXTA" appear in their name, without prior written permission of Sun.
30 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
32 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN
33 * MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
34 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
36 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
37 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
39 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 * JXTA is a registered trademark of Sun Microsystems, Inc. in the United
42 * States and other countries.
44 * Please see the license information page at :
45 * <http://www.jxta.org/project/www/license.html> for instructions on use of
46 * the license in source files.
48 * ====================================================================
50 * This software consists of voluntary contributions made by many individuals
51 * on behalf of Project JXTA. For more information on Project JXTA, please see
52 * http://www.jxta.org.
54 * This license is based on the BSD license adopted by the Apache Foundation.
56 package net.jxta.impl.endpoint.servlethttp;
58 import java.net.InetAddress;
59 import java.util.ArrayList;
60 import java.util.Comparator;
61 import java.util.Collections;
62 import java.util.Enumeration;
63 import java.util.Iterator;
64 import java.util.List;
66 import java.net.UnknownHostException;
67 import java.util.concurrent.Executor;
69 import java.util.logging.Logger;
70 import java.util.logging.Level;
72 import net.jxta.logging.Logging;
74 import net.jxta.document.Advertisement;
75 import net.jxta.document.AdvertisementFactory;
76 import net.jxta.document.Attribute;
77 import net.jxta.document.XMLElement;
78 import net.jxta.endpoint.EndpointAddress;
79 import net.jxta.endpoint.EndpointService;
80 import net.jxta.id.ID;
81 import net.jxta.meter.MonitorResources;
82 import net.jxta.peergroup.PeerGroup;
83 import net.jxta.protocol.ConfigParams;
84 import net.jxta.protocol.ModuleImplAdvertisement;
85 import net.jxta.protocol.TransportAdvertisement;
86 import net.jxta.platform.Module;
88 import net.jxta.exception.PeerGroupException;
90 import net.jxta.impl.endpoint.transportMeter.TransportBindingMeter;
91 import net.jxta.impl.endpoint.transportMeter.TransportMeter;
92 import net.jxta.impl.endpoint.transportMeter.TransportMeterBuildSettings;
93 import net.jxta.impl.endpoint.transportMeter.TransportServiceMonitor;
94 import net.jxta.impl.endpoint.IPUtils;
95 import net.jxta.impl.protocol.HTTPAdv;
97 import net.jxta.impl.meter.*;
98 import net.jxta.impl.peergroup.StdPeerGroup;
101 * A JXTA Message Transport
103 * <p/>This class is really a facade for the following:<ul>
104 * <li>An HTTP client message sender</li>
105 * <li>An HTTP-server-based message receiver</li>
108 public final class ServletHttpTransport implements Module {
113 private final static transient Logger LOG = Logger.getLogger(ServletHttpTransport.class.getName());
116 * The name of the protocol
118 private final static String DEFAULT_HTTP_PROTOCOL_NAME = "http";
120 String HTTP_PROTOCOL_NAME = DEFAULT_HTTP_PROTOCOL_NAME;
123 * PeerGroup we are working for
127 ModuleImplAdvertisement implAdvertisement;
130 * The executor used by HttpClientMessenger
135 * The endpoint we attach to.
137 private EndpointService endpoint = null;
140 * If {@code true} then we are configured to act as an HTTP client. This
141 * means we will poll for messages.
143 private boolean configClient = false;
146 * If {@code true} then we are configured to act as an HTTP server. This
147 * means we will accept connections from polling clients.
149 private boolean configServer = false;
152 * The HttpMessageSender instance
154 private HttpMessageSender sender = null;
157 * The HttpMessageReceiver instance
159 private HttpMessageReceiver receiver = null;
162 * The TransportMeter for this httpTransport
164 private TransportMeter transportMeter;
167 * The TransportBindingMeter for unknown connections (pings/errors)
169 private TransportBindingMeter unknownTransportBindingMeter;
172 * The InetAddress of the network interface we are bound to otherwise the
175 InetAddress usingInterface = null;
178 * Port number to which we are bound.
183 * The addresses which we will advertise and by which we may be reached.
185 private List<EndpointAddress> publicAddresses = null;
188 * Our preferred return address which we will use as the "source" for
191 private EndpointAddress publicAddress;
196 public synchronized void init(PeerGroup group, ID assignedID, Advertisement impl) throws PeerGroupException {
198 this.assignedID = assignedID;
199 implAdvertisement = (ModuleImplAdvertisement) impl;
201 this.executor = ((StdPeerGroup) group).getExecutor();
203 // Get out invariable parameters from the implAdv
204 XMLElement param = (XMLElement) implAdvertisement.getParam();
207 Enumeration list = param.getChildren("Proto");
209 if (list.hasMoreElements()) {
210 XMLElement pname = (XMLElement) list.nextElement();
211 String configProtoName = pname.getTextValue();
212 if (null != configProtoName) {
213 HTTP_PROTOCOL_NAME = configProtoName.trim();
218 ConfigParams peerAdv = group.getConfigAdvertisement();
220 param = (XMLElement) peerAdv.getServiceParam(assignedID);
222 Enumeration httpChilds = param.getChildren(TransportAdvertisement.getAdvertisementType());
224 // get the TransportAdv
225 if (httpChilds.hasMoreElements()) {
226 param = (XMLElement) httpChilds.nextElement();
227 Attribute typeAttr = param.getAttribute("type");
229 if (!HTTPAdv.getAdvertisementType().equals(typeAttr.getValue())) {
230 throw new IllegalArgumentException("Transport adv is not an http adv");
233 if (httpChilds.hasMoreElements()) {
234 throw new IllegalArgumentException("Configuration contained multiple http advertisements");
237 throw new IllegalArgumentException("Configuration did not contain http advertisement");
240 Advertisement paramsAdv = AdvertisementFactory.newAdvertisement(param);
242 if (!(paramsAdv instanceof HTTPAdv)) {
243 throw new IllegalArgumentException("Provided advertisement was not a " + HTTPAdv.getAdvertisementType());
246 HTTPAdv httpAdv = (HTTPAdv) paramsAdv;
248 // determine the local interface to use. If the user specifies
249 // one, use that. Otherwise, use the all the available interfaces.
250 String interfaceAddressStr = httpAdv.getInterfaceAddress();
251 boolean publicAddressOnly = httpAdv.getPublicAddressOnly();
253 if (interfaceAddressStr != null) {
255 usingInterface = InetAddress.getByName(interfaceAddressStr);
256 } catch (UnknownHostException failed) {
257 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
258 LOG.warning("Invalid address for local interface address, using default");
260 usingInterface = IPUtils.ANYADDRESS;
263 usingInterface = IPUtils.ANYADDRESS;
266 usingPort = httpAdv.getPort();
268 configClient = httpAdv.isClientEnabled();
270 configServer = httpAdv.isServerEnabled();
272 publicAddresses = getPublicAddresses(configServer, httpAdv.getServer(), usingInterface, usingPort, publicAddressOnly);
274 if (!configClient && !configServer) {
275 throw new IllegalArgumentException("Neither incoming nor outgoing connections configured.");
278 publicAddress = publicAddresses.get(0);
280 // Tell tell the world about our configuration.
281 if (Logging.SHOW_CONFIG && LOG.isLoggable(Level.CONFIG)) {
282 StringBuilder configInfo = new StringBuilder("Configuring HTTP Message Transport : " + assignedID);
284 if (implAdvertisement != null) {
285 configInfo.append("\n\tImplementation:");
286 configInfo.append("\n\t\tModule Spec ID: ").append(implAdvertisement.getModuleSpecID());
287 configInfo.append("\n\t\tImpl Description: ").append(implAdvertisement.getDescription());
288 configInfo.append("\n\t\tImpl URI: ").append(implAdvertisement.getUri());
289 configInfo.append("\n\t\tImpl Code: ").append(implAdvertisement.getCode());
291 configInfo.append("\n\tGroup Params:");
292 configInfo.append("\n\t\tGroup: ").append(group.getPeerGroupName());
293 configInfo.append("\n\t\tGroup ID: ").append(group.getPeerGroupID());
294 configInfo.append("\n\t\tPeer ID: ").append(group.getPeerID());
295 configInfo.append("\n\tConfiguration :");
296 configInfo.append("\n\t\tProtocol: ").append(httpAdv.getProtocol());
297 configInfo.append("\n\t\tClient Enabled: ").append(configClient);
298 configInfo.append("\n\t\tServer Enabled: ").append(configServer);
299 configInfo.append("\n\t\tPublic address: ").append(httpAdv.getServer() == null ? "(unspecified)" : httpAdv.getServer());
300 configInfo.append("\n\t\tInterface address: ").append(interfaceAddressStr == null ? "(unspecified)" : interfaceAddressStr);
301 configInfo.append("\n\t\tUnicast Server Bind Addr: ").append(IPUtils.getHostAddress(usingInterface)).append(":").append(usingPort);
302 configInfo.append("\n\t\tPublic Addresses: ");
303 configInfo.append("\n\t\t\tDefault Endpoint Addr : ").append(publicAddress);
304 for (EndpointAddress anAddr : publicAddresses) {
305 configInfo.append("\n\t\t\tEndpoint Addr : ").append(anAddr);
307 LOG.config(configInfo.toString());
314 public synchronized int startApp(String[] args) {
315 endpoint = group.getEndpointService();
317 if (null == endpoint) {
318 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
319 LOG.warning("Stalled until there is an endpoint service");
322 return START_AGAIN_STALLED;
325 if (TransportMeterBuildSettings.TRANSPORT_METERING) {
326 TransportServiceMonitor transportServiceMonitor = (TransportServiceMonitor) MonitorManager.getServiceMonitor(group,
327 MonitorResources.transportServiceMonitorClassID);
329 if (transportServiceMonitor != null) {
330 transportMeter = transportServiceMonitor.createTransportMeter("HTTP", publicAddress);
331 unknownTransportBindingMeter = transportMeter.getTransportBindingMeter(TransportMeter.UNKNOWN_PEER,
332 TransportMeter.UNKNOWN_ADDRESS);
337 // Start the http server that runs the receiver.
340 receiver = new HttpMessageReceiver(this, publicAddresses, usingInterface, usingPort);
342 } catch (PeerGroupException e) {
343 if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
344 LOG.log(Level.SEVERE, "Could not start http message receiver", e);
346 return -1; // Can't go on; if we were configured to be a server we must make the failure obvious.
351 // create the MessageSender
354 sender = new HttpMessageSender(this,
355 new EndpointAddress("jxta", group.getPeerID().getUniqueValue().toString(), null, null));
357 } catch (PeerGroupException e) {
358 if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
359 LOG.log(Level.SEVERE, "Could not start http message sender", e);
361 return -1; // Can't go on; if we were configured to be a server we must make the failure obvious.
371 public synchronized void stopApp() {
372 if (receiver != null) {
376 if (sender != null) {
386 EndpointService getEndpointService() {
390 private List<EndpointAddress> getPublicAddresses(boolean serverEnabled, String serverName, InetAddress usingInterface, int serverSocketPort, boolean publicAddressOnly) {
391 List<EndpointAddress> publicAddresses = new ArrayList<EndpointAddress>();
394 // Build the publicAddresses
396 // first in the list is the "public server name". We don't try to
397 // resolve this since it might not be resolvable in the context
398 // we are running in, we just assume it's good.
399 if (serverName != null) {
400 // use speced server name.
401 EndpointAddress newAddr = new EndpointAddress(HTTP_PROTOCOL_NAME, serverName, null, null);
403 publicAddresses.add(newAddr);
404 if (publicAddressOnly) {
405 return publicAddresses;
410 // then add the rest of the local interfaces as appropriate
411 if (usingInterface.equals(IPUtils.ANYADDRESS)) {
413 Iterator<InetAddress> eachLocal = IPUtils.getAllLocalAddresses();
414 List<EndpointAddress> wildAddrs = new ArrayList<EndpointAddress>();
416 while (eachLocal.hasNext()) {
417 InetAddress anAddress = eachLocal.next();
419 String hostAddress = IPUtils.getHostAddress(anAddress);
421 EndpointAddress newAddr = new EndpointAddress(HTTP_PROTOCOL_NAME,
422 hostAddress + ":" + Integer.toString(serverSocketPort), null, null);
424 // don't add it if its already in the list
425 if (!publicAddresses.contains(newAddr)) {
426 wildAddrs.add(newAddr);
430 // we sort them so that later equals() will be deterministic.
431 // the result of IPUtils.getAllLocalAddresses() is not known
433 Collections.sort(wildAddrs, new Comparator<EndpointAddress>() {
434 public int compare(EndpointAddress one, EndpointAddress two) {
435 return one.toString().compareTo(two.toString());
439 public boolean equals(Object that) {
444 publicAddresses.addAll(wildAddrs);
446 // use specified interface
447 String hostAddress = IPUtils.getHostAddress(usingInterface);
449 EndpointAddress newAddr = new EndpointAddress(HTTP_PROTOCOL_NAME,
450 hostAddress + ":" + Integer.toString(serverSocketPort), null, null);
452 // don't add it if its already in the list
453 if (!publicAddresses.contains(newAddr)) {
454 publicAddresses.add(newAddr);
458 return publicAddresses;
461 TransportBindingMeter getTransportBindingMeter(String peerIDString, EndpointAddress destinationAddress) {
462 if (transportMeter != null) {
463 return transportMeter.getTransportBindingMeter((peerIDString != null) ? peerIDString : TransportMeter.UNKNOWN_PEER,
470 TransportBindingMeter getUnknownTransportBindingMeter() {
471 return unknownTransportBindingMeter;