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 }