View Javadoc

1   /**
2    *
3    * Copyright 2003-2004 The Apache Software Foundation
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.geronimo.jetty;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.io.OutputStream;
22  import java.net.URI;
23  import java.net.URISyntaxException;
24  import java.util.HashMap;
25  import java.util.Map;
26  
27  import org.apache.geronimo.security.ContextManager;
28  import org.apache.geronimo.webservices.WebServiceContainer;
29  import org.mortbay.http.Authenticator;
30  import org.mortbay.http.BasicAuthenticator;
31  import org.mortbay.http.ClientCertAuthenticator;
32  import org.mortbay.http.DigestAuthenticator;
33  import org.mortbay.http.HttpContext;
34  import org.mortbay.http.HttpException;
35  import org.mortbay.http.HttpHandler;
36  import org.mortbay.http.HttpRequest;
37  import org.mortbay.http.HttpResponse;
38  
39  /**
40   * Delegates requests to a WebServiceContainer which is presumably for an EJB WebService.
41   * <p/>
42   * WebServiceContainer delegates to an EJBContainer that will ultimately provide the JNDI,
43   * TX, and Security services for this web service.
44   * <p/>
45   * Nothing stopping us from using this for POJOs or other types of webservices if shared
46   * Context (JNDI, tx, security) wasn't required to be supplied by the web context.
47   * <p/>
48   * From a 10,000 foot view the Jetty architecture has:
49   * Container -> Context -> Holder -> Servlet
50   * <p/>
51   * A Container has multiple Contexts, typically webapps
52   * A Context provides the JNDI, TX, and Security for the webapp and has many Holders
53   * A Holder simply wraps each Servlet
54   * <p/>
55   * The POJO Web Service architecture on Jetty looks like this:
56   * Container -> WebApp Context -> JettyPOJOWebServiceHolder -> POJOWebServiceServlet
57   * <p/>
58   * The EJB Web Service architecure, on the other hand, creates one Context for each EJB:
59   * Container -> JettyEJBWebServiceContext
60   *
61   * @version $Rev: 431706 $ $Date: 2006-08-15 14:19:27 -0700 (Tue, 15 Aug 2006) $
62   */
63  public class JettyEJBWebServiceContext extends HttpContext implements HttpHandler {
64  
65      private final String contextPath;
66      private final WebServiceContainer webServiceContainer;
67      private final Authenticator authenticator;
68      private final JAASJettyRealm realm;
69      private final boolean isConfidentialTransportGuarantee;
70      private final boolean isIntegralTransportGuarantee;
71      private final ClassLoader classLoader;
72  
73      private HttpContext httpContext;
74  
75      public JettyEJBWebServiceContext(String contextPath, WebServiceContainer webServiceContainer, InternalJAASJettyRealm internalJAASJettyRealm, String realmName, String transportGuarantee, String authMethod, ClassLoader classLoader) {
76          this.contextPath = contextPath;
77          this.webServiceContainer = webServiceContainer;
78          if (internalJAASJettyRealm != null) {
79              JAASJettyRealm realm = new JAASJettyRealm(realmName, internalJAASJettyRealm);
80              setRealm(realm);
81              this.realm = realm;
82              if ("NONE".equals(transportGuarantee)) {
83                  isConfidentialTransportGuarantee = false;
84                  isIntegralTransportGuarantee = false;
85              } else if ("INTEGRAL".equals(transportGuarantee)) {
86                  isConfidentialTransportGuarantee = false;
87                  isIntegralTransportGuarantee = true;
88              } else if ("CONFIDENTIAL".equals(transportGuarantee)) {
89                  isConfidentialTransportGuarantee = true;
90                  isIntegralTransportGuarantee = false;
91              } else {
92                  throw new IllegalArgumentException("Invalid transport-guarantee: " + transportGuarantee);
93              }
94              if ("BASIC".equals(authMethod)) {
95                  authenticator = new BasicAuthenticator();
96              } else if ("DIGEST".equals(authMethod)) {
97                  authenticator = new DigestAuthenticator();
98              } else if ("CLIENT-CERT".equals(authMethod)) {
99                  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 }