001: /*
002: * <copyright>
003: *
004: * Copyright 2000-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.redirect;
028:
029: import java.io.IOException;
030: import java.net.URI;
031: import java.util.Collections;
032: import java.util.List;
033: import java.util.Map;
034: import javax.servlet.ServletException;
035: import javax.servlet.http.HttpServletRequest;
036: import javax.servlet.http.HttpServletResponse;
037:
038: import org.cougaar.bootstrap.SystemProperties;
039: import org.cougaar.core.component.Component;
040: import org.cougaar.core.component.ServiceBroker;
041: import org.cougaar.core.component.ServiceProvider;
042: import org.cougaar.core.service.LoggingService;
043: import org.cougaar.util.Arguments;
044: import org.cougaar.util.GenericStateModelAdapter;
045:
046: /**
047: * This component is a standard {@link ServletRedirectorService} that emits a
048: * client-side HTTP redirect.
049: *
050: * @property org.cougaar.lib.web.redirect.naming_timeout=30000
051: * Timeout in millseconds for "/$" remote redirect lookups, where
052: * 0 indicates no timeout.
053: */
054: public class HttpServletRedirector extends GenericStateModelAdapter
055: implements Component {
056:
057: protected ServiceBroker sb;
058:
059: protected LoggingService log;
060:
061: private ServletRedirectorService srs;
062: private ServletRedirectorRegistryService srrs;
063: private ServiceProvider srs_sp;
064:
065: protected Arguments args = Arguments.EMPTY_INSTANCE;
066: protected long naming_timeout;
067:
068: public void setServiceBroker(ServiceBroker sb) {
069: this .sb = sb;
070: }
071:
072: public void setParameter(Object o) {
073: args = new Arguments(o);
074: }
075:
076: public void load() {
077: super .load();
078:
079: // parse args
080: String prefix = "org.cougaar.lib.web.redirect.";
081: naming_timeout = args.getLong("naming_timeout",
082: SystemProperties.getLong(prefix + "naming_timeout",
083: 30000));
084:
085: // get services
086: log = (LoggingService) sb.getService(this ,
087: LoggingService.class, null);
088:
089: // create and advertise our service
090: srs = new ServletRedirectorService() {
091: public int redirect(String encName, List options,
092: NamingSupport namingSupport,
093: HttpServletRequest req, HttpServletResponse res)
094: throws ServletException, IOException {
095: return HttpServletRedirector.this .redirect(encName,
096: options, namingSupport, req, res);
097: }
098:
099: public String toString() {
100: return HttpServletRedirector.this .toString();
101: }
102: };
103: srrs = (ServletRedirectorRegistryService) sb.getService(this ,
104: ServletRedirectorRegistryService.class, null);
105: if (srrs != null) {
106: // share registry
107: if (log.isInfoEnabled()) {
108: log.info("Adding redirector to registry");
109: }
110: srrs.add(srs);
111: } else {
112: // just us
113: if (log.isInfoEnabled()) {
114: log.info("Advertising a new redirect service");
115: }
116: final Class cl = ServletRedirectorService.class;
117: srs_sp = new ServiceProvider() {
118: public Object getService(ServiceBroker sb, Object req,
119: Class scl) {
120: return (cl.isAssignableFrom(scl) ? srs : null);
121: }
122:
123: public void releaseService(ServiceBroker sb,
124: Object req, Class scl, Object svc) {
125: }
126: };
127: sb.addService(cl, srs_sp);
128: }
129: }
130:
131: public void unload() {
132: // remove/revoke our service
133: if (srs != null) {
134: if (srrs != null) {
135: srrs.remove(srs);
136: srrs = null;
137: } else if (srs_sp != null) {
138: sb
139: .revokeService(ServletRedirectorService.class,
140: srs_sp);
141: srs_sp = null;
142: }
143: srs = null;
144: }
145:
146: // release the logger
147: if (log != null) {
148: sb.releaseService(this , LoggingService.class, log);
149: log = null;
150: }
151:
152: super .unload();
153: }
154:
155: protected boolean isSupported(String s) {
156: return ("http_redirect".equals(s) || "^".equals(s));
157: }
158:
159: protected int redirect(String encName, List options,
160: NamingSupport namingSupport, HttpServletRequest req,
161: HttpServletResponse res) throws ServletException,
162: IOException {
163:
164: // see if we're enabled
165: if (options != null) {
166: boolean is_supported = false;
167: for (int i = 0; i < options.size(); i++) {
168: String s = (String) options.get(i);
169: if (isSupported(s)) {
170: is_supported = true;
171: break;
172: }
173: }
174: if (!is_supported) {
175: return ServletRedirector.NOT_SUPPORTED;
176: }
177: }
178:
179: // do the naming lookup
180: Map uris = namingSupport.getNamingEntries(encName,
181: naming_timeout);
182: if (uris == null) {
183: uris = Collections.EMPTY_MAP;
184: }
185:
186: // see if the remote target supports our scheme
187: String scheme = req.getScheme();
188: URI uri = (URI) uris.get(scheme);
189: if (uri == null) {
190: // check for protocol switch
191: scheme = ("http".equals(scheme) ? "https" : "https"
192: .equals(scheme) ? "http" : null);
193: uri = (URI) uris.get(scheme);
194: if (uri == null) {
195: // no matching naming service entries
196: return ServletRedirector.NO_NAMING_ENTRIES;
197: }
198: }
199:
200: String host = uri.getHost();
201: int port = uri.getPort();
202: String basePath = uri.getPath();
203:
204: // check for a trivial loop back to ourself
205: String localHost = req.getServerName();
206: int localPort = req.getServerPort();
207: if ((host == null ? localHost == null : host.equals(localHost))
208: && (port == localPort)) {
209: return ServletRedirector.DETECTED_LOOP;
210: }
211:
212: // create path with correct contextPath.
213: // e.g. from local "/foo/$bar" to remote "/qux/$bar"
214: String path = req.getRequestURI();
215: String contextPath = req.getContextPath();
216: if (contextPath != null && contextPath.length() > 0) {
217: path = path.substring(contextPath.length());
218: }
219: if (basePath != null && basePath.length() > 0) {
220: path = basePath + path;
221: }
222:
223: // get query string, preserve as-is
224: String queryString = req.getQueryString();
225: queryString = (queryString == null ? "" : ("?" + queryString));
226:
227: // create the new location string
228: String location = scheme + "://" + host + ":" + port + path
229: + queryString;
230:
231: // redirect the request to the remote location
232: doRedirect(encName, location, req, res);
233:
234: return ServletRedirector.REDIRECTED;
235: }
236:
237: /**
238: * Do the redirect.
239: * <p>
240: * A subclass could instead tunnel, etc.
241: */
242: protected void doRedirect(String encName, String location,
243: HttpServletRequest req, HttpServletResponse res)
244: throws ServletException, IOException {
245:
246: if (log.isInfoEnabled()) {
247: log.info("Redirecting request for " + encName + " to "
248: + location);
249: }
250:
251: // encode for redirect -- typically a no-op
252: String encLoc = res.encodeRedirectURL(location);
253:
254: res.sendRedirect(encLoc);
255: }
256:
257: }
|