001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 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.axis;
028:
029: import java.io.BufferedReader;
030: import java.io.ByteArrayInputStream;
031: import java.io.IOException;
032: import java.io.InputStream;
033: import java.io.UnsupportedEncodingException;
034: import java.net.InetAddress;
035: import java.security.Principal;
036: import java.util.Enumeration;
037: import java.util.Locale;
038: import java.util.Map;
039:
040: import javax.servlet.RequestDispatcher;
041: import javax.servlet.Servlet;
042: import javax.servlet.ServletConfig;
043: import javax.servlet.ServletContext;
044: import javax.servlet.ServletException;
045: import javax.servlet.ServletInputStream;
046: import javax.servlet.ServletRequest;
047: import javax.servlet.ServletResponse;
048: import javax.servlet.http.Cookie;
049: import javax.servlet.http.HttpServletRequest;
050: import javax.servlet.http.HttpSession;
051:
052: import org.apache.axis.EngineConfiguration;
053: import org.apache.axis.WSDDEngineConfiguration;
054: import org.apache.axis.deployment.wsdd.WSDDDeployment;
055: import org.apache.axis.deployment.wsdd.WSDDDocument;
056: import org.apache.axis.server.AxisServer;
057: import org.apache.axis.transport.http.AxisServlet;
058: import org.apache.axis.utils.XMLUtils;
059:
060: import org.cougaar.core.component.Component;
061: import org.cougaar.core.component.ServiceBroker;
062: import org.cougaar.core.component.ServiceProvider;
063: import org.cougaar.core.node.NodeControlService;
064: import org.cougaar.core.service.LoggingService;
065: import org.cougaar.core.service.ServletService;
066: import org.cougaar.core.service.WebServicesService;
067: import org.cougaar.util.GenericStateModelAdapter;
068:
069: import org.w3c.dom.Document;
070:
071: /**
072: * This component advertises the {@link WebServicesService}
073: * that uses the <a href="http://ws.apache.org/axis">Axis</a>
074: * SOAP engine and a "/axis/services" {@link ServletService}
075: * path.
076: * <p>
077: * Load with:<pre>
078: * <component
079: * class="org.cougaar.lib.web.axis.WebServicesProvider"/>
080: * </pre>
081: * <p>
082: * Axis requires the following configuration files to be
083: * installed in the $TOMCAT_HOME directory. These are currently
084: * included in the Cougaar "webtomcat" module:<pre>
085: * $CIP/webtomcat/data/webapps/ROOT/WEB-INF/server-config.wsdd
086: * $CIP/webtomcat/data/webapps/ROOT/WEB-INF/attachments
087: * </pre>
088: */
089: public final class WebServicesProvider extends GenericStateModelAdapter
090: implements Component {
091:
092: private static final String AXIS_SERVLET_PATH = "/axis/services";
093:
094: private static final String ATTR_AXIS_ENGINE = "AxisEngine";
095:
096: private ServiceBroker sb;
097: private ServiceBroker rootsb;
098:
099: private LoggingService log;
100: private ServletService servletService;
101:
102: private ServiceProvider wssp;
103:
104: private AxisServer axisEngine;
105:
106: public void setServiceBroker(ServiceBroker sb) {
107: this .sb = sb;
108: }
109:
110: public void load() {
111: super .load();
112:
113: log = (LoggingService) sb.getService(this ,
114: LoggingService.class, null);
115:
116: // get the node-level service broker if it's available
117: NodeControlService ncs = (NodeControlService) sb.getService(
118: this , NodeControlService.class, null);
119: if (ncs != null) {
120: rootsb = ncs.getRootServiceBroker();
121: sb.releaseService(this , NodeControlService.class, ncs);
122: }
123:
124: servletService = (ServletService) sb.getService(this ,
125: ServletService.class, null);
126: if (servletService == null) {
127: throw new RuntimeException(
128: "Unable to obtain ServletService");
129: }
130:
131: final Servlet axisServlet = new AxisServlet();
132:
133: // create proxy servlet so we can get our hands on the engine
134: Servlet proxyServlet = new Servlet() {
135: public void init(ServletConfig config)
136: throws ServletException {
137: // pass in the real config; another option is to pass in a
138: // proxy, to keep the engine private to our servlet.
139: axisServlet.init(config);
140: // note that the axisServlet "init" creates the axis engine,
141: // so we save it now
142: WebServicesProvider.this .saveAxisEngine(config);
143: }
144:
145: public ServletConfig getServletConfig() {
146: return axisServlet.getServletConfig();
147: }
148:
149: public void service(ServletRequest req, ServletResponse res)
150: throws ServletException, IOException {
151: // create a request proxy to replace Cougaar's empty
152: // content-path with Axis's expected "/axis" content-path.
153: // This proxy also makes room for future enhancements.
154: ServletRequest reqProxy = (req instanceof HttpServletRequest ? (new RequestProxy(
155: (HttpServletRequest) req))
156: : req);
157: axisServlet.service(reqProxy, res);
158: }
159:
160: public String getServletInfo() {
161: return axisServlet.getServletInfo();
162: }
163:
164: public void destroy() {
165: axisServlet.destroy();
166: }
167: };
168:
169: try {
170: servletService.register(AXIS_SERVLET_PATH, proxyServlet);
171: } catch (Exception e) {
172: throw new RuntimeException("Unable to register \""
173: + AXIS_SERVLET_PATH + "\"", e);
174: }
175:
176: wssp = new WSSP();
177: ServiceBroker the_sb = (rootsb == null ? sb : rootsb);
178: the_sb.addService(WebServicesService.class, wssp);
179:
180: if (log.isInfoEnabled()) {
181: String localHost;
182: try {
183: InetAddress localAddr = InetAddress.getLocalHost();
184: localHost = localAddr.getHostName();
185: } catch (Exception e) {
186: localHost = "localhost";
187: }
188: int httpPort = servletService.getHttpPort();
189: int httpsPort = servletService.getHttpsPort();
190: String url = "http" + (httpPort > 0 ? "" : "s") + "://"
191: + localHost + ":"
192: + (httpPort > 0 ? httpPort : httpsPort)
193: + AXIS_SERVLET_PATH;
194: log.info("Advertised WebServicesService, listening on "
195: + url);
196: }
197: }
198:
199: public void unload() {
200: super .unload();
201:
202: ServiceBroker the_sb = (rootsb == null ? sb : rootsb);
203: the_sb.revokeService(WebServicesService.class, wssp);
204: wssp = null;
205:
206: if (servletService != null) {
207: // this unregisters our servlet
208: sb.releaseService(this , ServletService.class,
209: servletService);
210: servletService = null;
211: }
212:
213: if (log != null) {
214: sb.releaseService(this , LoggingService.class, log);
215: log = null;
216: }
217: }
218:
219: /**
220: * After AxisServlet init, get the Axis engine from the config.
221: */
222: private void saveAxisEngine(ServletConfig config) {
223: // get our axisEngine from the servlet context.
224: //
225: // This engine be used in subsequent multithreaded client
226: // "processWSDD" calls. For init we're single-threaded, so no
227: // lock is required.
228: ServletContext context = config.getServletContext();
229: synchronized (context) {
230: Object contextObject = context
231: .getAttribute(ATTR_AXIS_ENGINE);
232: if (contextObject instanceof AxisServer) {
233: axisEngine = (AxisServer) contextObject;
234: }
235: }
236:
237: if (axisEngine == null) {
238: if (log.isErrorEnabled()) {
239: log.error("Axis servlet \"init(" + config
240: + ")\" lacks axis engine" + " attribute \""
241: + ATTR_AXIS_ENGINE + "\"");
242: }
243: } else {
244: if (log.isInfoEnabled()) {
245: log.info("Initialized Axis, found engine: "
246: + axisEngine);
247: }
248: }
249: }
250:
251: private void processWSDD(String s) {
252: processWSDD(new ByteArrayInputStream(s.getBytes()));
253: }
254:
255: private void processWSDD(InputStream is) {
256: // from org.apache.axis.utils.Admin "processWSDD":
257: Document doc;
258: try {
259: doc = XMLUtils.newDocument(is);
260: } catch (Exception e) {
261: throw new RuntimeException("Unable to parse XML", e);
262: }
263: processWSDD(doc);
264: }
265:
266: private void processWSDD(Document doc) {
267: if (axisEngine == null) {
268: throw new RuntimeException(
269: "Unable to processWSDD, axisEngine is null!");
270: }
271:
272: // from org.apache.axis.utils.Admin "processWSDD":
273: try {
274: WSDDDocument wsddDoc = new WSDDDocument(doc);
275: EngineConfiguration config = axisEngine.getConfig();
276: if (config instanceof WSDDEngineConfiguration) {
277: WSDDDeployment deployment = ((WSDDEngineConfiguration) config)
278: .getDeployment();
279: wsddDoc.deploy(deployment);
280: }
281: axisEngine.refreshGlobalOptions();
282: } catch (Exception e) {
283: throw new RuntimeException("Failed processWSDD(\n"
284: + docToString(doc) + "\n)", e);
285: }
286:
287: // don't do:
288: // axisEngine.saveConfiguration();
289: // since we don't want to modify the server-config.wsdd with
290: // these internal webservices. Actually, Axis should see
291: // that the file is read-only anyways.
292:
293: if (log.isInfoEnabled()) {
294: log.info("Successfully processed WSDD:\n"
295: + docToString(doc));
296: }
297: }
298:
299: private static final String docToString(Document doc) {
300: try {
301: return XMLUtils.DocumentToString(doc);
302: } catch (Exception e) {
303: return e.getMessage();
304: }
305: }
306:
307: private class WSSP implements ServiceProvider {
308: private final WebServicesService SERVICE_INSTANCE = new WSSI();
309:
310: public Object getService(ServiceBroker sb, Object requestor,
311: Class serviceClass) {
312: if (WebServicesService.class.isAssignableFrom(serviceClass)) {
313: return SERVICE_INSTANCE;
314: } else {
315: return null;
316: }
317: }
318:
319: public void releaseService(ServiceBroker sb, Object requestor,
320: Class serviceClass, Object service) {
321: // ideally we'd undeploy any deployments created by any given
322: // service instance, but it'd be a bit awkward to implement...
323: }
324:
325: private final class WSSI implements WebServicesService {
326: public void processWSDD(String s) {
327: WebServicesProvider.this .processWSDD(s);
328: }
329:
330: public void processWSDD(InputStream is) {
331: WebServicesProvider.this .processWSDD(is);
332: }
333:
334: public void processWSDD(Document doc) {
335: WebServicesProvider.this .processWSDD(doc);
336: }
337: }
338: }
339:
340: /**
341: * Dumb proxy for the HttpServletRequest that forwards all calls
342: * except "getContextPath()", which is hard-coded to return "/axis".
343: * <p>
344: * We only do this so the "/axis/services" page's "?wsdl" links
345: * will work, otherwise the links are printed as "/services"
346: * without the "/axis" prefix.
347: */
348: private static final class RequestProxy implements
349: HttpServletRequest {
350: private final HttpServletRequest req;
351:
352: public RequestProxy(HttpServletRequest req) {
353: this .req = req;
354: }
355:
356: public String getContextPath() {
357: // Cougaar's "ROOT" is "/", which confuses Axis, so here we
358: // hard-code the context-path to the standard Axis "webapps"
359: // directory name of "/axis".
360: return "/axis";
361: }
362:
363: // forward the rest!
364:
365: // ServletRequest:
366: public Object getAttribute(String name) {
367: return req.getAttribute(name);
368: }
369:
370: public Enumeration getAttributeNames() {
371: return req.getAttributeNames();
372: }
373:
374: public String getCharacterEncoding() {
375: return req.getCharacterEncoding();
376: }
377:
378: public void setCharacterEncoding(String env)
379: throws UnsupportedEncodingException {
380: req.setCharacterEncoding(env);
381: }
382:
383: public int getContentLength() {
384: return req.getContentLength();
385: }
386:
387: public String getContentType() {
388: return req.getContentType();
389: }
390:
391: public ServletInputStream getInputStream() throws IOException {
392: return req.getInputStream();
393: }
394:
395: public String getParameter(String name) {
396: return req.getParameter(name);
397: }
398:
399: public Enumeration getParameterNames() {
400: return req.getParameterNames();
401: }
402:
403: public String[] getParameterValues(String name) {
404: return req.getParameterValues(name);
405: }
406:
407: public Map getParameterMap() {
408: return req.getParameterMap();
409: }
410:
411: public String getProtocol() {
412: return req.getProtocol();
413: }
414:
415: public String getScheme() {
416: return req.getScheme();
417: }
418:
419: public String getServerName() {
420: return req.getServerName();
421: }
422:
423: public int getServerPort() {
424: return req.getServerPort();
425: }
426:
427: public BufferedReader getReader() throws IOException {
428: return req.getReader();
429: }
430:
431: public String getRemoteAddr() {
432: return req.getRemoteAddr();
433: }
434:
435: public String getRemoteHost() {
436: return req.getRemoteHost();
437: }
438:
439: public void setAttribute(String name, Object o) {
440: req.setAttribute(name, o);
441: }
442:
443: public void removeAttribute(String name) {
444: req.removeAttribute(name);
445: }
446:
447: public Locale getLocale() {
448: return req.getLocale();
449: }
450:
451: public Enumeration getLocales() {
452: return req.getLocales();
453: }
454:
455: public boolean isSecure() {
456: return req.isSecure();
457: }
458:
459: public RequestDispatcher getRequestDispatcher(String path) {
460: return req.getRequestDispatcher(path);
461: }
462:
463: /** @deprecated */
464: public String getRealPath(String path) {
465: return req.getRealPath(path);
466: }
467:
468: // HttpServletRequest:
469: public String getAuthType() {
470: return req.getAuthType();
471: }
472:
473: public Cookie[] getCookies() {
474: return req.getCookies();
475: }
476:
477: public long getDateHeader(String name) {
478: return req.getDateHeader(name);
479: }
480:
481: public String getHeader(String name) {
482: return req.getHeader(name);
483: }
484:
485: public Enumeration getHeaders(String name) {
486: return req.getHeaders(name);
487: }
488:
489: public Enumeration getHeaderNames() {
490: return req.getHeaderNames();
491: }
492:
493: public int getIntHeader(String name) {
494: return req.getIntHeader(name);
495: }
496:
497: public String getMethod() {
498: return req.getMethod();
499: }
500:
501: public String getPathInfo() {
502: return req.getPathInfo();
503: }
504:
505: public String getPathTranslated() {
506: return req.getPathTranslated();
507: }
508:
509: public String getQueryString() {
510: return req.getQueryString();
511: }
512:
513: public String getRemoteUser() {
514: return req.getRemoteUser();
515: }
516:
517: public boolean isUserInRole(String role) {
518: return req.isUserInRole(role);
519: }
520:
521: public java.security.Principal getUserPrincipal() {
522: return req.getUserPrincipal();
523: }
524:
525: public String getRequestedSessionId() {
526: return req.getRequestedSessionId();
527: }
528:
529: public String getRequestURI() {
530: return req.getRequestURI();
531: }
532:
533: public StringBuffer getRequestURL() {
534: return req.getRequestURL();
535: }
536:
537: public String getServletPath() {
538: return req.getServletPath();
539: }
540:
541: public HttpSession getSession(boolean create) {
542: return req.getSession(create);
543: }
544:
545: public HttpSession getSession() {
546: return req.getSession();
547: }
548:
549: public boolean isRequestedSessionIdValid() {
550: return req.isRequestedSessionIdValid();
551: }
552:
553: public boolean isRequestedSessionIdFromCookie() {
554: return req.isRequestedSessionIdFromCookie();
555: }
556:
557: public boolean isRequestedSessionIdFromURL() {
558: return req.isRequestedSessionIdFromURL();
559: }
560:
561: /** @deprecated */
562: public boolean isRequestedSessionIdFromUrl() {
563: return req.isRequestedSessionIdFromUrl();
564: }
565: }
566: }
|