001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2007 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.lib.web.micro.mts;
028:
029: import java.io.IOException;
030: import java.io.UnsupportedEncodingException;
031: import java.net.URI;
032: import java.net.URLDecoder;
033: import java.util.Collections;
034: import java.util.List;
035: import java.util.Map;
036: import javax.servlet.ServletException;
037: import javax.servlet.http.HttpServletRequest;
038: import javax.servlet.http.HttpServletResponse;
039:
040: import org.cougaar.bootstrap.SystemProperties;
041: import org.cougaar.core.agent.service.MessageSwitchService;
042: import org.cougaar.core.component.Component;
043: import org.cougaar.core.component.ServiceBroker;
044: import org.cougaar.core.component.ServiceProvider;
045: import org.cougaar.core.mts.MessageAddress;
046: import org.cougaar.core.node.NodeIdentificationService;
047: import org.cougaar.core.service.LoggingService;
048: import org.cougaar.core.service.ThreadService;
049: import org.cougaar.core.service.UIDService;
050: import org.cougaar.lib.web.micro.base.Connection;
051: import org.cougaar.lib.web.micro.base.ServletTunnel;
052: import org.cougaar.lib.web.redirect.NamingSupport;
053: import org.cougaar.lib.web.redirect.ServletRedirector;
054: import org.cougaar.lib.web.redirect.ServletRedirectorService;
055: import org.cougaar.lib.web.redirect.ServletRedirectorRegistryService;
056: import org.cougaar.util.Arguments;
057: import org.cougaar.util.GenericStateModelAdapter;
058:
059: /**
060: * This component loads the "mts_tunnel" servlet redirector, which tunnels
061: * servlet requests through the message transport to a remote mts-engine.
062: *
063: * @property org.cougaar.lib.web.micro.mts.tunnel.nagle=1000
064: * @property org.cougaar.lib.web.micro.mts.tunnel.naming_timeout=30000
065: */
066: public class MessagingServletTunnel extends GenericStateModelAdapter
067: implements Component {
068:
069: private ServiceBroker sb;
070:
071: private Arguments args = Arguments.EMPTY_INSTANCE;
072:
073: private LoggingService log;
074: private UIDService uids;
075: private ThreadService threadService;
076: private MessageSwitchService messageSwitch;
077:
078: private long naming_timeout;
079:
080: private ServletRedirectorService srs;
081: private ServletRedirectorRegistryService srrs;
082: private ServiceProvider srs_sp;
083:
084: private String localNode;
085: private MessagingClientFactory client_factory;
086:
087: public void setServiceBroker(ServiceBroker sb) {
088: this .sb = sb;
089: }
090:
091: public void setParameter(Object o) {
092: args = new Arguments(o);
093: }
094:
095: public void load() {
096: super .load();
097:
098: // parse args
099: String prefix = "org.cougaar.lib.web.micro.mts.tunnel.";
100: long nagle = args.getLong("nagle", SystemProperties.getLong(
101: prefix + "nagle", 1000));
102: naming_timeout = args.getLong("naming_timeout",
103: SystemProperties.getLong(prefix + "naming_timeout",
104: 30000));
105:
106: // obtain services
107: log = (LoggingService) sb.getService(this ,
108: LoggingService.class, null);
109: uids = (UIDService) sb.getService(this , UIDService.class, null);
110: threadService = (ThreadService) sb.getService(this ,
111: ThreadService.class, null);
112: messageSwitch = (MessageSwitchService) sb.getService(this ,
113: MessageSwitchService.class, null);
114:
115: // figure out which node we're in
116: NodeIdentificationService nis = (NodeIdentificationService) sb
117: .getService(this , NodeIdentificationService.class, null);
118: if (nis != null) {
119: localNode = nis.getMessageAddress().getAddress();
120: sb.releaseService(this , NodeIdentificationService.class,
121: nis);
122: }
123:
124: // create connection factory
125: client_factory = new MessagingClientFactory(log, uids,
126: threadService, messageSwitch, nagle);
127: client_factory.start();
128:
129: if (log.isInfoEnabled()) {
130: log.info("Started MTS servlet tunnel");
131: }
132:
133: // create and advertise our service
134: srs = new ServletRedirectorService() {
135: public int redirect(String encName, List options,
136: NamingSupport namingSupport,
137: HttpServletRequest req, HttpServletResponse res)
138: throws ServletException, IOException {
139: return MessagingServletTunnel.this .redirect(encName,
140: options, namingSupport, req, res);
141: }
142: };
143: srrs = (ServletRedirectorRegistryService) sb.getService(this ,
144: ServletRedirectorRegistryService.class, null);
145: if (srrs != null) {
146: // share registry
147: srrs.add(srs);
148: } else {
149: // just us
150: final Class cl = ServletRedirectorService.class;
151: srs_sp = new ServiceProvider() {
152: public Object getService(ServiceBroker sb, Object req,
153: Class scl) {
154: return (cl.isAssignableFrom(scl) ? srs : null);
155: }
156:
157: public void releaseService(ServiceBroker sb,
158: Object req, Class scl, Object svc) {
159: }
160: };
161: sb.addService(cl, srs_sp);
162: }
163: }
164:
165: public void unload() {
166: // remove/revoke our service
167: if (srs != null) {
168: if (srrs != null) {
169: srrs.remove(srs);
170: srrs = null;
171: } else if (srs_sp != null) {
172: sb
173: .revokeService(ServletRedirectorService.class,
174: srs_sp);
175: srs_sp = null;
176: }
177: srs = null;
178: }
179:
180: // stop mts listener
181: if (client_factory != null) {
182: client_factory.stop();
183: client_factory = null;
184: }
185:
186: // release services
187: if (messageSwitch != null) {
188: sb.releaseService(this , MessageSwitchService.class,
189: messageSwitch);
190: messageSwitch = null;
191: }
192: if (threadService != null) {
193: sb.releaseService(this , ThreadService.class, threadService);
194: threadService = null;
195: }
196: if (uids != null) {
197: sb.releaseService(this , UIDService.class, uids);
198: uids = null;
199: }
200: if (log != null) {
201: sb.releaseService(this , LoggingService.class, log);
202: log = null;
203: }
204:
205: super .unload();
206: }
207:
208: private boolean isSupported(String s) {
209: return ("mts_tunnel".equals(s) || "_".equals(s));
210: }
211:
212: private int redirect(String encName, List options,
213: NamingSupport namingSupport, HttpServletRequest req,
214: HttpServletResponse res) throws ServletException,
215: IOException {
216:
217: // see if we're enabled
218: if (options != null) {
219: boolean is_supported = false;
220: for (int i = 0; i < options.size(); i++) {
221: String s = (String) options.get(i);
222: if (isSupported(s)) {
223: is_supported = true;
224: break;
225: }
226: }
227: if (!is_supported) {
228: return ServletRedirector.NOT_SUPPORTED;
229: }
230: }
231:
232: // do the naming lookup
233: Map uris = namingSupport.getNamingEntries(encName,
234: naming_timeout);
235: if (uris == null) {
236: uris = Collections.EMPTY_MAP;
237: }
238:
239: // see if the remote target can receive tunnel requests
240: URI uri = (URI) uris.get("mts_tunnel");
241: if (uri == null) {
242: return ServletRedirector.NO_NAMING_ENTRIES;
243: }
244:
245: // extract remote node name
246: String encNode = uri.getPath();
247: if (encNode == null) {
248: encNode = "";
249: }
250: if (encNode.length() > 0 && encNode.charAt(0) == '/') {
251: encNode = encNode.substring(1);
252: }
253: String rawNode = decode(encNode);
254: if (rawNode.length() <= 0) {
255: // invalid entry, shouldn't happen
256: return ServletRedirector.OTHER_ERROR;
257: }
258: MessageAddress addr = MessageAddress.getMessageAddress(rawNode);
259:
260: // check for loopback error, possibly due to stale naming entries.
261: if (addr.equals(localNode)) {
262: return ServletRedirector.DETECTED_LOOP;
263: }
264:
265: // tunnel
266: Map metaData = ServletTunnel.extractMetaData(req);
267: Connection con = client_factory.connect(addr, metaData);
268: ServletTunnel.tunnel(req, res, con);
269: return ServletRedirector.REDIRECTED;
270: }
271:
272: private static final String decode(String enc) {
273: try {
274: return URLDecoder.decode(enc, "UTF-8");
275: } catch (UnsupportedEncodingException uee) {
276: throw new RuntimeException("Invalid name: " + enc, uee);
277: }
278: }
279: }
|