001 /** 002 * 003 * Copyright 2003-2004 The Apache Software Foundation 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.geronimo.jetty; 018 019 import java.io.IOException; 020 import java.io.InputStream; 021 import java.io.OutputStream; 022 import java.net.URI; 023 import java.net.URISyntaxException; 024 import java.util.HashMap; 025 import java.util.Map; 026 027 import org.apache.geronimo.security.ContextManager; 028 import org.apache.geronimo.webservices.WebServiceContainer; 029 import org.mortbay.http.Authenticator; 030 import org.mortbay.http.BasicAuthenticator; 031 import org.mortbay.http.ClientCertAuthenticator; 032 import org.mortbay.http.DigestAuthenticator; 033 import org.mortbay.http.HttpContext; 034 import org.mortbay.http.HttpException; 035 import org.mortbay.http.HttpHandler; 036 import org.mortbay.http.HttpRequest; 037 import org.mortbay.http.HttpResponse; 038 039 /** 040 * Delegates requests to a WebServiceContainer which is presumably for an EJB WebService. 041 * <p/> 042 * WebServiceContainer delegates to an EJBContainer that will ultimately provide the JNDI, 043 * TX, and Security services for this web service. 044 * <p/> 045 * Nothing stopping us from using this for POJOs or other types of webservices if shared 046 * Context (JNDI, tx, security) wasn't required to be supplied by the web context. 047 * <p/> 048 * From a 10,000 foot view the Jetty architecture has: 049 * Container -> Context -> Holder -> Servlet 050 * <p/> 051 * A Container has multiple Contexts, typically webapps 052 * A Context provides the JNDI, TX, and Security for the webapp and has many Holders 053 * A Holder simply wraps each Servlet 054 * <p/> 055 * The POJO Web Service architecture on Jetty looks like this: 056 * Container -> WebApp Context -> JettyPOJOWebServiceHolder -> POJOWebServiceServlet 057 * <p/> 058 * The EJB Web Service architecure, on the other hand, creates one Context for each EJB: 059 * Container -> JettyEJBWebServiceContext 060 * 061 * @version $Rev: 431706 $ $Date: 2006-08-15 14:19:27 -0700 (Tue, 15 Aug 2006) $ 062 */ 063 public class JettyEJBWebServiceContext extends HttpContext implements HttpHandler { 064 065 private final String contextPath; 066 private final WebServiceContainer webServiceContainer; 067 private final Authenticator authenticator; 068 private final JAASJettyRealm realm; 069 private final boolean isConfidentialTransportGuarantee; 070 private final boolean isIntegralTransportGuarantee; 071 private final ClassLoader classLoader; 072 073 private HttpContext httpContext; 074 075 public JettyEJBWebServiceContext(String contextPath, WebServiceContainer webServiceContainer, InternalJAASJettyRealm internalJAASJettyRealm, String realmName, String transportGuarantee, String authMethod, ClassLoader classLoader) { 076 this.contextPath = contextPath; 077 this.webServiceContainer = webServiceContainer; 078 if (internalJAASJettyRealm != null) { 079 JAASJettyRealm realm = new JAASJettyRealm(realmName, internalJAASJettyRealm); 080 setRealm(realm); 081 this.realm = realm; 082 if ("NONE".equals(transportGuarantee)) { 083 isConfidentialTransportGuarantee = false; 084 isIntegralTransportGuarantee = false; 085 } else if ("INTEGRAL".equals(transportGuarantee)) { 086 isConfidentialTransportGuarantee = false; 087 isIntegralTransportGuarantee = true; 088 } else if ("CONFIDENTIAL".equals(transportGuarantee)) { 089 isConfidentialTransportGuarantee = true; 090 isIntegralTransportGuarantee = false; 091 } else { 092 throw new IllegalArgumentException("Invalid transport-guarantee: " + transportGuarantee); 093 } 094 if ("BASIC".equals(authMethod)) { 095 authenticator = new BasicAuthenticator(); 096 } else if ("DIGEST".equals(authMethod)) { 097 authenticator = new DigestAuthenticator(); 098 } else if ("CLIENT-CERT".equals(authMethod)) { 099 authenticator = new ClientCertAuthenticator(); 100 } else if ("NONE".equals(authMethod)) { 101 authenticator = null; 102 } else { 103 throw new IllegalArgumentException("Invalid authMethod: " + authMethod); 104 } 105 } else { 106 realm = null; 107 authenticator = null; 108 isConfidentialTransportGuarantee = false; 109 isIntegralTransportGuarantee = false; 110 } 111 this.classLoader = classLoader; 112 } 113 114 public String getName() { 115 //need a better name 116 return contextPath; 117 } 118 119 public HttpContext getHttpContext() { 120 return httpContext; 121 } 122 123 public void initialize(HttpContext httpContext) { 124 this.httpContext = httpContext; 125 } 126 127 public void handle(HttpRequest req, HttpResponse res) throws HttpException, IOException { 128 req.setContentType("text/xml"); 129 RequestAdapter request = new RequestAdapter(req); 130 ResponseAdapter response = new ResponseAdapter(res); 131 132 if (req.getParameter("wsdl") != null) { 133 try { 134 webServiceContainer.getWsdl(request, response); 135 //WHO IS RESPONSIBLE FOR CLOSING OUT? 136 } catch (IOException e) { 137 throw e; 138 } catch (Exception e) { 139 throw (HttpException) new HttpException(500, "Could not fetch wsdl!").initCause(e); 140 } 141 } else { 142 if (isConfidentialTransportGuarantee) { 143 if (!req.isConfidential()) { 144 throw new HttpException(403); 145 } 146 } else if (isIntegralTransportGuarantee) { 147 if (!req.isIntegral()) { 148 throw new HttpException(403); 149 } 150 } 151 Thread currentThread = Thread.currentThread(); 152 ClassLoader oldClassLoader = currentThread.getContextClassLoader(); 153 currentThread.setContextClassLoader(classLoader); 154 //hard to imagine this could be anything but null, but.... 155 // Subject oldSubject = ContextManager.getCurrentCaller(); 156 try { 157 if (authenticator != null) { 158 String pathInContext = org.mortbay.util.URI.canonicalPath(req.getPath()); 159 if (authenticator.authenticate(realm, pathInContext, req, res) == null) { 160 throw new HttpException(403); 161 } 162 } else { 163 //EJB will figure out correct defaultSubject shortly 164 //TODO consider replacing the GenericEJBContainer.DefaultSubjectInterceptor with this line 165 //setting the defaultSubject. 166 ContextManager.popCallers(null); 167 } 168 try { 169 webServiceContainer.invoke(request, response); 170 req.setHandled(true); 171 } catch (IOException e) { 172 throw e; 173 } catch (Exception e) { 174 throw (HttpException) new HttpException(500, "Could not process message!").initCause(e); 175 } 176 } finally { 177 // ContextManager.setCurrentCaller(oldSubject); 178 currentThread.setContextClassLoader(oldClassLoader); 179 } 180 } 181 182 } 183 184 public String getContextPath() { 185 return contextPath; 186 } 187 188 public String getSecurityRealmName() { 189 if (realm == null) { 190 return null; 191 } else { 192 return realm.getSecurityRealmName(); 193 } 194 } 195 196 public static class RequestAdapter implements WebServiceContainer.Request { 197 private final HttpRequest request; 198 private URI uri; 199 200 public RequestAdapter(HttpRequest request) { 201 this.request = request; 202 } 203 204 public String getHeader(String name) { 205 return request.getField(name); 206 } 207 208 public java.net.URI getURI() { 209 if (uri == null) { 210 try { 211 String uriString = request.getScheme() + "://" + request.getHost() + ":" + request.getPort() + request.getURI(); 212 //return new java.net.URI(uri.getScheme(),uri.getHost(),uri.getPath(),uri.); 213 uri = new java.net.URI(uriString); 214 } catch (URISyntaxException e) { 215 throw new IllegalStateException(e.getMessage()); 216 } 217 } 218 return uri; 219 } 220 221 public int getContentLength() { 222 return request.getContentLength(); 223 } 224 225 public String getContentType() { 226 return request.getContentType(); 227 } 228 229 public InputStream getInputStream() throws IOException { 230 return request.getInputStream(); 231 } 232 233 public int getMethod() { 234 Integer method = (Integer) methods.get(request.getMethod()); 235 return method == null ? UNSUPPORTED : method.intValue(); 236 } 237 238 public String getParameter(String name) { 239 return request.getParameter(name); 240 } 241 242 public Map getParameters() { 243 return request.getParameters(); 244 } 245 246 public Object getAttribute(String name) { 247 return request.getAttribute(name); 248 } 249 250 public void setAttribute(String name, Object value) { 251 request.setAttribute(name, value); 252 } 253 254 255 private static final Map methods = new HashMap(); 256 257 static { 258 methods.put("OPTIONS", new Integer(OPTIONS)); 259 methods.put("GET", new Integer(GET)); 260 methods.put("HEAD", new Integer(HEAD)); 261 methods.put("POST", new Integer(POST)); 262 methods.put("PUT", new Integer(PUT)); 263 methods.put("DELETE", new Integer(DELETE)); 264 methods.put("TRACE", new Integer(TRACE)); 265 methods.put("CONNECT", new Integer(CONNECT)); 266 } 267 268 } 269 270 public static class ResponseAdapter implements WebServiceContainer.Response { 271 private final HttpResponse response; 272 273 public ResponseAdapter(HttpResponse response) { 274 this.response = response; 275 } 276 277 public void setHeader(String name, String value) { 278 response.setField(name, value); 279 } 280 281 public String getHeader(String name) { 282 return response.getField(name); 283 } 284 285 public OutputStream getOutputStream() { 286 return response.getOutputStream(); 287 } 288 289 public void setStatusCode(int code) { 290 response.setStatus(code); 291 } 292 293 public int getStatusCode() { 294 return response.getStatus(); 295 } 296 297 public void setContentType(String type) { 298 response.setContentType(type); 299 } 300 301 public String getContentType() { 302 return response.getContentType(); 303 } 304 305 public void setStatusMessage(String responseString) { 306 response.setStatus(response.getStatus(), responseString); 307 } 308 } 309 310 }