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.tomcat;
017:
018: import java.beans.PropertyChangeListener;
019: import java.io.IOException;
020: import java.util.HashMap;
021: import java.util.List;
022: import java.util.Map;
023:
024: import javax.security.auth.Subject;
025: import javax.security.jacc.PolicyContext;
026: import javax.servlet.Servlet;
027: import javax.servlet.ServletException;
028:
029: import org.apache.catalina.Container;
030: import org.apache.catalina.LifecycleException;
031: import org.apache.catalina.LifecycleListener;
032: import org.apache.catalina.Loader;
033: import org.apache.catalina.Manager;
034: import org.apache.catalina.Valve;
035: import org.apache.catalina.Wrapper;
036: import org.apache.catalina.connector.Request;
037: import org.apache.catalina.connector.Response;
038: import org.apache.catalina.core.StandardContext;
039: import org.apache.catalina.ha.CatalinaCluster;
040: import org.apache.catalina.valves.ValveBase;
041: import org.apache.geronimo.common.DeploymentException;
042: import org.apache.geronimo.common.GeronimoSecurityException;
043: import org.apache.geronimo.security.ContextManager;
044: import org.apache.geronimo.security.jacc.RunAsSource;
045: import org.apache.geronimo.tomcat.interceptor.BeforeAfter;
046: import org.apache.geronimo.tomcat.interceptor.ComponentContextBeforeAfter;
047: import org.apache.geronimo.tomcat.interceptor.InstanceContextBeforeAfter;
048: import org.apache.geronimo.tomcat.interceptor.PolicyContextBeforeAfter;
049: import org.apache.geronimo.tomcat.interceptor.UserTransactionBeforeAfter;
050: import org.apache.geronimo.tomcat.listener.RunAsInstanceListener;
051: import org.apache.geronimo.tomcat.util.SecurityHolder;
052: import org.apache.geronimo.tomcat.valve.DefaultSubjectValve;
053: import org.apache.geronimo.tomcat.valve.GeronimoBeforeAfterValve;
054: import org.apache.geronimo.webservices.POJOWebServiceServlet;
055: import org.apache.geronimo.webservices.WebServiceContainer;
056: import org.apache.geronimo.webservices.WebServiceContainerInvoker;
057:
058: /**
059: * @version $Rev: 620623 $ $Date: 2008-02-11 13:51:02 -0800 (Mon, 11 Feb 2008) $
060: */
061: public class GeronimoStandardContext extends StandardContext {
062:
063: private static final long serialVersionUID = 3834587716552831032L;
064:
065: private Subject defaultSubject = null;
066: private RunAsSource runAsSource = RunAsSource.NULL;
067:
068: private Map webServiceMap = null;
069:
070: private boolean pipelineInitialized;
071:
072: private BeforeAfter beforeAfter = null;
073: private int contextCount = 0;
074:
075: private static final boolean allowLinking;
076:
077: static {
078: allowLinking = new Boolean(
079: System
080: .getProperty(
081: "org.apache.geronimo.tomcat.GeronimoStandardContext.allowLinking",
082: "false"));
083: }
084:
085: public void setContextProperties(TomcatContext ctx)
086: throws DeploymentException {
087:
088: // Create ReadOnlyContext
089: javax.naming.Context enc = ctx.getJndiContext();
090: setInstanceManager(ctx.getInstanceManager());
091:
092: //try to make sure this mbean properties match those of the TomcatWebAppContext
093: if (ctx instanceof TomcatWebAppContext) {
094: TomcatWebAppContext tctx = (TomcatWebAppContext) ctx;
095: setJavaVMs(tctx.getJavaVMs());
096: setServer(tctx.getServer());
097: setJ2EEApplication(tctx.getJ2EEApplication());
098: setJ2EEServer(tctx.getJ2EEServer());
099: //install jasper injection support if required
100: if (tctx.getRuntimeCustomizer() != null) {
101: Map<String, Object> servletContext = new HashMap<String, Object>();
102: Map<Class, Object> customizerContext = new HashMap<Class, Object>();
103: customizerContext.put(Map.class, servletContext);
104: customizerContext.put(javax.naming.Context.class, enc);
105: tctx.getRuntimeCustomizer()
106: .customize(customizerContext);
107: for (Map.Entry<String, Object> entry : servletContext
108: .entrySet()) {
109: getServletContext().setAttribute(entry.getKey(),
110: entry.getValue());
111: }
112: }
113: }
114:
115: int index = 0;
116: BeforeAfter interceptor = new InstanceContextBeforeAfter(null,
117: index++, index++, ctx.getUnshareableResources(), ctx
118: .getApplicationManagedSecurityResources(), ctx
119: .getTrackedConnectionAssociator());
120:
121: // Set ComponentContext BeforeAfter
122: if (enc != null) {
123: interceptor = new ComponentContextBeforeAfter(interceptor,
124: index++, enc);
125: }
126:
127: //Set a PolicyContext BeforeAfter
128: SecurityHolder securityHolder = ctx.getSecurityHolder();
129: if (securityHolder != null) {
130:
131: // save the role designates for mapping servlets to their run-as roles
132: runAsSource = securityHolder.getRunAsSource();
133:
134: if (securityHolder.getPolicyContextID() != null) {
135:
136: PolicyContext.setContextID(securityHolder
137: .getPolicyContextID());
138:
139: /**
140: * Register our default subject with the ContextManager
141: */
142: defaultSubject = securityHolder.getDefaultSubject();
143:
144: if (defaultSubject == null) {
145: defaultSubject = ContextManager.EMPTY;
146: }
147:
148: interceptor = new PolicyContextBeforeAfter(interceptor,
149: index++, index++, index++, securityHolder
150: .getPolicyContextID(), defaultSubject);
151:
152: }
153: }
154:
155: //Set a UserTransactionBeforeAfter
156: interceptor = new UserTransactionBeforeAfter(interceptor,
157: index++, ctx.getUserTransaction());
158:
159: Valve clusteredValve = ctx.getClusteredValve();
160: if (null != clusteredValve) {
161: addValve(clusteredValve);
162: }
163:
164: //Set the BeforeAfters as a valve
165: GeronimoBeforeAfterValve geronimoBAValve = new GeronimoBeforeAfterValve(
166: interceptor, index);
167: addValve(geronimoBAValve);
168: beforeAfter = interceptor;
169: contextCount = index;
170:
171: //Not clear if user defined valves should be involved in init processing. Probably not since
172: //request and response are null.
173:
174: addValve(new SystemMethodValve());
175:
176: // Add User Defined Valves
177: List valveChain = ctx.getValveChain();
178: if (valveChain != null) {
179: for (Object valve : valveChain) {
180: addValve((Valve) valve);
181: }
182: }
183:
184: // Add User Defined Listeners
185: List listenerChain = ctx.getLifecycleListenerChain();
186: if (listenerChain != null) {
187: for (Object listener : listenerChain) {
188: addLifecycleListener((LifecycleListener) listener);
189: }
190: }
191:
192: CatalinaCluster cluster = ctx.getCluster();
193: if (cluster != null)
194: this .setCluster(cluster);
195:
196: Manager manager = ctx.getManager();
197: if (manager != null)
198: this .setManager(manager);
199:
200: pipelineInitialized = true;
201: this .webServiceMap = ctx.getWebServices();
202:
203: this .setCrossContext(ctx.isCrossContext());
204:
205: this .setWorkDir(ctx.getWorkDir());
206:
207: super .setAllowLinking(allowLinking);
208:
209: this .setCookies(!ctx.isDisableCookies());
210:
211: //Set the Dispatch listener
212: this
213: .addInstanceListener("org.apache.geronimo.tomcat.listener.DispatchListener");
214:
215: //Set the run-as listener. listeners must be added before start() is called
216: if (runAsSource != null) {
217: this .addInstanceListener(RunAsInstanceListener.class
218: .getName());
219: }
220: }
221:
222: /* This method is called by a background thread to destroy sessions (among other things)
223: * so we need to apply appropriate context to the thread to expose JNDI, etc.
224: */
225: public void backgroundProcess() {
226: Object context[] = null;
227:
228: if (beforeAfter != null) {
229: context = new Object[contextCount];
230: beforeAfter.before(context, null, null,
231: BeforeAfter.EDGE_SERVLET);
232: }
233:
234: try {
235: super .backgroundProcess();
236: } finally {
237: if (beforeAfter != null) {
238: beforeAfter.after(context, null, null, 0);
239: }
240: }
241: }
242:
243: public void kill() throws Exception {
244: Object context[] = null;
245:
246: if (beforeAfter != null) {
247: context = new Object[contextCount];
248: beforeAfter.before(context, null, null,
249: BeforeAfter.EDGE_SERVLET);
250: }
251:
252: try {
253: stop();
254: destroy();
255: } finally {
256: if (beforeAfter != null) {
257: beforeAfter.after(context, null, null, 0);
258: }
259: }
260: }
261:
262: public synchronized void start() throws LifecycleException {
263: if (pipelineInitialized) {
264: try {
265: Valve valve = getFirst();
266: valve.invoke(null, null);
267: //Install the DefaultSubjectValve after the authentication valve so the default subject is supplied
268: //only if no real subject is authenticated.
269:
270: Valve defaultSubjectValve = new DefaultSubjectValve(
271: defaultSubject);
272: addValve(defaultSubjectValve);
273:
274: // if a servlet uses run-as then make sure role desgnates have been provided
275: if (hasRunAsServlet()) {
276: if (runAsSource == null) {
277: throw new GeronimoSecurityException(
278: "web.xml or annotation specifies a run-as role but no subject configuration supplied for run-as roles");
279: }
280: } else {
281: // optimization
282: this
283: .removeInstanceListener(RunAsInstanceListener.class
284: .getName());
285: }
286:
287: } catch (IOException e) {
288: if (e.getCause() instanceof LifecycleException) {
289: throw (LifecycleException) e.getCause();
290: }
291: throw new LifecycleException(e);
292: } catch (ServletException e) {
293: throw new LifecycleException(e);
294: }
295: } else
296: super .start();
297: }
298:
299: public void addChild(Container child) {
300: Wrapper wrapper = (Wrapper) child;
301:
302: String servletClassName = wrapper.getServletClass();
303: if (servletClassName == null) {
304: super .addChild(child);
305: return;
306: }
307:
308: ClassLoader cl = this .getParentClassLoader();
309:
310: Class baseServletClass;
311: Class servletClass;
312: try {
313: baseServletClass = cl.loadClass(Servlet.class.getName());
314: servletClass = cl.loadClass(servletClassName);
315: //Check if the servlet is of type Servlet class
316: if (!baseServletClass.isAssignableFrom(servletClass)) {
317: //Nope - its probably a webservice, so lets see...
318: if (webServiceMap != null) {
319: WebServiceContainer webServiceContainer = (WebServiceContainer) webServiceMap
320: .get(wrapper.getName());
321:
322: if (webServiceContainer != null) {
323: //Yep its a web service
324: //So swap it out with a POJOWebServiceServlet
325: wrapper
326: .setServletClass("org.apache.geronimo.webservices.POJOWebServiceServlet");
327:
328: //Set the WebServiceContainer stuff
329: String webServicecontainerID = wrapper
330: .getName()
331: + WebServiceContainerInvoker.WEBSERVICE_CONTAINER
332: + webServiceContainer.hashCode();
333: getServletContext().setAttribute(
334: webServicecontainerID,
335: webServiceContainer);
336: wrapper
337: .addInitParameter(
338: WebServiceContainerInvoker.WEBSERVICE_CONTAINER,
339: webServicecontainerID);
340:
341: //Set the SEI Class in the attribute
342: String pojoClassID = wrapper.getName()
343: + POJOWebServiceServlet.POJO_CLASS
344: + servletClass.hashCode();
345: getServletContext().setAttribute(pojoClassID,
346: servletClass);
347: wrapper.addInitParameter(
348: POJOWebServiceServlet.POJO_CLASS,
349: pojoClassID);
350: }
351: }
352: }
353: } catch (ClassNotFoundException e) {
354: throw new RuntimeException(e.getMessage(), e);
355: }
356:
357: super .addChild(child);
358: }
359:
360: public synchronized void setLoader(final Loader delegate) {
361: Loader loader = new Loader() {
362:
363: public void backgroundProcess() {
364: delegate.backgroundProcess();
365: }
366:
367: public ClassLoader getClassLoader() {
368: // Implementation Note: the actual CL to be used by this
369: // context is the Geronimo one and not the Tomcat one.
370: return parentClassLoader;
371: }
372:
373: public Container getContainer() {
374: return delegate.getContainer();
375: }
376:
377: public void setContainer(Container container) {
378: delegate.setContainer(container);
379: }
380:
381: public boolean getDelegate() {
382: return delegate.getDelegate();
383: }
384:
385: public void setDelegate(boolean delegateBoolean) {
386: delegate.setDelegate(delegateBoolean);
387: }
388:
389: public String getInfo() {
390: return delegate.getInfo();
391: }
392:
393: public boolean getReloadable() {
394: return false;
395: }
396:
397: public void setReloadable(boolean reloadable) {
398: if (reloadable) {
399: throw new UnsupportedOperationException(
400: "Reloadable context is not supported.");
401: }
402: }
403:
404: public void addPropertyChangeListener(
405: PropertyChangeListener listener) {
406: delegate.addPropertyChangeListener(listener);
407: }
408:
409: public void addRepository(String repository) {
410: delegate.addRepository(repository);
411: }
412:
413: public String[] findRepositories() {
414: return delegate.findRepositories();
415: }
416:
417: public boolean modified() {
418: return delegate.modified();
419: }
420:
421: public void removePropertyChangeListener(
422: PropertyChangeListener listener) {
423: delegate.removePropertyChangeListener(listener);
424: }
425: };
426:
427: super .setLoader(loader);
428: }
429:
430: private class SystemMethodValve extends ValveBase {
431:
432: public void invoke(Request request, Response response)
433: throws IOException, ServletException {
434: if (request == null && response == null) {
435: try {
436: GeronimoStandardContext.super .start();
437: } catch (LifecycleException e) {
438: throw (IOException) new IOException(
439: "wrapping lifecycle exception")
440: .initCause(e);
441: }
442: if (GeronimoStandardContext.this .getState() != 1
443: || !GeronimoStandardContext.this .getAvailable()) {
444: throw new IOException(
445: "Context did not start for an unknown reason");
446: }
447: } else {
448: getNext().invoke(request, response);
449: }
450:
451: }
452: }
453:
454: public BeforeAfter getBeforeAfter() {
455: return beforeAfter;
456: }
457:
458: public int getContextCount() {
459: return contextCount;
460: }
461:
462: /**
463: * Determine if the context has at least one servlet that specifies a run-as role
464: * @return true if at least one servlet specifies a run-as role, false otherwise
465: */
466: protected boolean hasRunAsServlet() {
467: for (Container servlet : findChildren()) {
468: if (servlet instanceof Wrapper) {
469: if (((Wrapper) servlet).getRunAs() != null) {
470: return true;
471: }
472: }
473: }
474: return false;
475: }
476:
477: /**
478: * Get the Subject for the servlet's run-as role
479: * @param runAsRole Name of run as role to get Subject for
480: * @return Subject for the servlet's run-as role, if specified. otherwise null.
481: */
482: public Subject getSubjectForRole(String runAsRole) {
483: return runAsSource.getSubjectForRole(runAsRole);
484: }
485: }
|