001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. 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: */package org.apache.geronimo.jetty6;
017:
018: import java.io.IOException;
019: import java.io.InputStream;
020: import java.io.OutputStream;
021: import java.net.URI;
022: import java.net.URISyntaxException;
023: import java.util.HashMap;
024: import java.util.Map;
025:
026: import javax.servlet.ServletException;
027: import javax.servlet.http.HttpServletRequest;
028: import javax.servlet.http.HttpServletResponse;
029:
030: import org.apache.geronimo.security.ContextManager;
031: import org.apache.geronimo.webservices.WebServiceContainer;
032: import org.mortbay.jetty.HttpException;
033: import org.mortbay.jetty.Request;
034: import org.mortbay.jetty.Response;
035: import org.mortbay.jetty.handler.ContextHandler;
036: import org.mortbay.jetty.security.Authenticator;
037: import org.mortbay.jetty.security.BasicAuthenticator;
038: import org.mortbay.jetty.security.ClientCertAuthenticator;
039: import org.mortbay.jetty.security.DigestAuthenticator;
040:
041: /**
042: * Delegates requests to a WebServiceContainer which is presumably for an EJB WebService.
043: * <p/>
044: * WebServiceContainer delegates to an EJBContainer that will ultimately provide the JNDI,
045: * TX, and Security services for this web service.
046: * <p/>
047: * Nothing stopping us from using this for POJOs or other types of webservices if shared
048: * Context (JNDI, tx, security) wasn't required to be supplied by the web context.
049: * <p/>
050: * From a 10,000 foot view the Jetty architecture has:
051: * Container -> Context -> Holder -> Servlet
052: * <p/>
053: * A Container has multiple Contexts, typically webapps
054: * A Context provides the JNDI, TX, and Security for the webapp and has many Holders
055: * A Holder simply wraps each Servlet
056: * <p/>
057: * The POJO Web Service architecture on Jetty looks like this:
058: * Container -> WebApp Context -> JettyPOJOWebServiceHolder -> POJOWebServiceServlet
059: * <p/>
060: * The EJB Web Service architecure, on the other hand, creates one Context for each EJB:
061: * Container -> JettyEJBWebServiceContext
062: *
063: * @version $Rev: 549825 $ $Date: 2007-06-22 07:17:31 -0700 (Fri, 22 Jun 2007) $
064: */
065: public class JettyEJBWebServiceContext extends ContextHandler {
066:
067: private final String contextPath;
068: private final WebServiceContainer webServiceContainer;
069: private final Authenticator authenticator;
070: private final JAASJettyRealm realm;
071: private final boolean isConfidentialTransportGuarantee;
072: private final boolean isIntegralTransportGuarantee;
073: private final ClassLoader classLoader;
074:
075: public JettyEJBWebServiceContext(String contextPath,
076: WebServiceContainer webServiceContainer,
077: InternalJAASJettyRealm internalJAASJettyRealm,
078: String realmName, String transportGuarantee,
079: String authMethod, ClassLoader classLoader) {
080: this .contextPath = contextPath;
081: this .webServiceContainer = webServiceContainer;
082: this .setContextPath(contextPath);
083:
084: if (internalJAASJettyRealm != null) {
085: realm = new JAASJettyRealm(realmName,
086: internalJAASJettyRealm);
087: //TODO
088: //not used???
089: //setUserRealm(realm);
090: // this.realm = realm;
091: if ("NONE".equals(transportGuarantee)) {
092: isConfidentialTransportGuarantee = false;
093: isIntegralTransportGuarantee = false;
094: } else if ("INTEGRAL".equals(transportGuarantee)) {
095: isConfidentialTransportGuarantee = false;
096: isIntegralTransportGuarantee = true;
097: } else if ("CONFIDENTIAL".equals(transportGuarantee)) {
098: isConfidentialTransportGuarantee = true;
099: isIntegralTransportGuarantee = false;
100: } else {
101: throw new IllegalArgumentException(
102: "Invalid transport-guarantee: "
103: + transportGuarantee);
104: }
105: if ("BASIC".equals(authMethod)) {
106: authenticator = new BasicAuthenticator();
107: } else if ("DIGEST".equals(authMethod)) {
108: authenticator = new DigestAuthenticator();
109: } else if ("CLIENT-CERT".equals(authMethod)) {
110: authenticator = new ClientCertAuthenticator();
111: } else if ("NONE".equals(authMethod)) {
112: authenticator = null;
113: } else {
114: throw new IllegalArgumentException(
115: "Invalid authMethod: " + authMethod);
116: }
117: } else {
118: realm = null;
119: authenticator = null;
120: isConfidentialTransportGuarantee = false;
121: isIntegralTransportGuarantee = false;
122: }
123: this .classLoader = classLoader;
124: }
125:
126: public String getName() {
127: //need a better name
128: return contextPath;
129: }
130:
131: public void handle(String target, HttpServletRequest req,
132: HttpServletResponse res, int dispatch) throws IOException,
133: ServletException {
134: //TODO
135: //do we need to check that this request should be handled by this handler?
136: if (!target.startsWith(contextPath)) {
137: return;
138: }
139: Request jettyRequest = (Request) req;
140: Response jettyResponse = (Response) res;
141: res.setContentType("text/xml");
142: RequestAdapter request = new RequestAdapter(jettyRequest);
143: ResponseAdapter response = new ResponseAdapter(jettyResponse);
144:
145: request.setAttribute(WebServiceContainer.SERVLET_REQUEST, req);
146: request.setAttribute(WebServiceContainer.SERVLET_RESPONSE, res);
147: // TODO: add support for context
148: request.setAttribute(WebServiceContainer.SERVLET_CONTEXT, null);
149:
150: if (req.getParameter("wsdl") != null) {
151: try {
152: webServiceContainer.getWsdl(request, response);
153: jettyRequest.setHandled(true);
154: } catch (IOException e) {
155: throw e;
156: } catch (Exception e) {
157: throw (HttpException) new HttpException(500,
158: "Could not fetch wsdl!").initCause(e);
159: }
160: } else {
161: if (isConfidentialTransportGuarantee) {
162: if (!req.isSecure()) {
163: throw new HttpException(403, null);
164: }
165: } else if (isIntegralTransportGuarantee) {
166: if (!jettyRequest.getConnection().isIntegral(
167: jettyRequest)) {
168: throw new HttpException(403, null);
169: }
170: }
171: Thread currentThread = Thread.currentThread();
172: ClassLoader oldClassLoader = currentThread
173: .getContextClassLoader();
174: currentThread.setContextClassLoader(classLoader);
175: //hard to imagine this could be anything but null, but....
176: // Subject oldSubject = ContextManager.getCurrentCaller();
177: try {
178: if (authenticator != null) {
179: String pathInContext = org.mortbay.util.URIUtil
180: .canonicalPath(req.getContextPath());
181: if (authenticator.authenticate(realm,
182: pathInContext, jettyRequest, jettyResponse) == null) {
183: throw new HttpException(403, null);
184: }
185: } else {
186: //EJB will figure out correct defaultSubject shortly
187: //TODO consider replacing the GenericEJBContainer.DefaultSubjectInterceptor with this line
188: //setting the defaultSubject.
189: ContextManager.popCallers(null);
190: }
191: try {
192: webServiceContainer.invoke(request, response);
193: jettyRequest.setHandled(true);
194: } catch (IOException e) {
195: throw e;
196: } catch (Exception e) {
197: throw (HttpException) new HttpException(500,
198: "Could not process message!").initCause(e);
199: }
200: } finally {
201: // ContextManager.setCurrentCaller(oldSubject);
202: currentThread.setContextClassLoader(oldClassLoader);
203: }
204: }
205:
206: }
207:
208: public String getContextPath() {
209: return contextPath;
210: }
211:
212: public String getSecurityRealmName() {
213: if (realm == null) {
214: return null;
215: } else {
216: return realm.getSecurityRealmName();
217: }
218: }
219:
220: public static class RequestAdapter implements
221: WebServiceContainer.Request {
222: private final Request request;
223: private URI uri;
224:
225: public RequestAdapter(Request request) {
226: this .request = request;
227: }
228:
229: public String getHeader(String name) {
230: return request.getHeader(name);
231: }
232:
233: public java.net.URI getURI() {
234: if (uri == null) {
235: try {
236: //String uriString = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getRequestURI();
237: //return new java.net.URI(uri.getScheme(),uri.getHost(),uri.getPath(),uri.);
238: uri = new java.net.URI(request.getScheme(), null,
239: request.getServerName(), request
240: .getServerPort(), request
241: .getRequestURI(), request
242: .getQueryString(), null);
243: } catch (URISyntaxException e) {
244: throw new IllegalStateException(e.getMessage(), e);
245: }
246: }
247: return uri;
248: }
249:
250: public int getContentLength() {
251: return request.getContentLength();
252: }
253:
254: public String getContentType() {
255: return request.getContentType();
256: }
257:
258: public InputStream getInputStream() throws IOException {
259: return request.getInputStream();
260: }
261:
262: public int getMethod() {
263: Integer method = (Integer) methods.get(request.getMethod());
264: return method == null ? UNSUPPORTED : method.intValue();
265: }
266:
267: public String getParameter(String name) {
268: return request.getParameter(name);
269: }
270:
271: public Map getParameters() {
272: return request.getParameterMap();
273: }
274:
275: public Object getAttribute(String name) {
276: return request.getAttribute(name);
277: }
278:
279: public void setAttribute(String name, Object value) {
280: request.setAttribute(name, value);
281: }
282:
283: public String getRemoteAddr() {
284: return request.getRemoteAddr();
285: }
286:
287: public String getContextPath() {
288: //request.getContextPath() isn't working correctly and returned null.
289: //use getRequestURI() for now before it is fixed.
290: //return request.getContextPath();
291: return request.getRequestURI();
292: }
293:
294: private static final Map methods = new HashMap();
295:
296: static {
297: methods.put("OPTIONS", new Integer(OPTIONS));
298: methods.put("GET", new Integer(GET));
299: methods.put("HEAD", new Integer(HEAD));
300: methods.put("POST", new Integer(POST));
301: methods.put("PUT", new Integer(PUT));
302: methods.put("DELETE", new Integer(DELETE));
303: methods.put("TRACE", new Integer(TRACE));
304: methods.put("CONNECT", new Integer(CONNECT));
305: }
306:
307: }
308:
309: public static class ResponseAdapter implements
310: WebServiceContainer.Response {
311: private final Response response;
312:
313: public ResponseAdapter(Response response) {
314: this .response = response;
315: }
316:
317: public void setHeader(String name, String value) {
318: response.setHeader(name, value);
319: }
320:
321: public String getHeader(String name) {
322: return response.getHeader(name);
323: }
324:
325: public OutputStream getOutputStream() {
326: try {
327: return response.getOutputStream();
328: } catch (IOException e) {
329: throw new IllegalStateException(e.getMessage(), e);
330: }
331: }
332:
333: public void setStatusCode(int code) {
334: response.setStatus(code);
335: }
336:
337: public int getStatusCode() {
338: return response.getStatus();
339: }
340:
341: public void setContentType(String type) {
342: response.setContentType(type);
343: }
344:
345: public String getContentType() {
346: return response.getContentType();
347: }
348:
349: public void setStatusMessage(String responseString) {
350: response.setStatus(response.getStatus(), responseString);
351: }
352:
353: public void flushBuffer() throws java.io.IOException {
354: response.flushBuffer();
355: }
356: }
357:
358: }
|