001    /**
002     *
003     * Licensed to the Apache Software Foundation (ASF) under one or more
004     * contributor license agreements.  See the NOTICE file distributed with
005     * this work for additional information regarding copyright ownership.
006     * The ASF licenses this file to You under the Apache License, Version 2.0
007     * (the "License"); you may not use this file except in compliance with
008     * the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *  Unless required by applicable law or agreed to in writing, software
013     *  distributed under the License is distributed on an "AS IS" BASIS,
014     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     *  See the License for the specific language governing permissions and
016     *  limitations under the License.
017     */
018    package org.apache.geronimo.openejb;
019    
020    import javax.naming.Context;
021    import javax.resource.ResourceException;
022    import javax.security.auth.Subject;
023    import javax.security.jacc.PolicyContext;
024    
025    import org.apache.commons.logging.Log;
026    import org.apache.commons.logging.LogFactory;
027    import org.apache.geronimo.connector.outbound.connectiontracking.ConnectorInstanceContext;
028    import org.apache.geronimo.connector.outbound.connectiontracking.ConnectorInstanceContextImpl;
029    import org.apache.geronimo.connector.outbound.connectiontracking.TrackedConnectionAssociator;
030    import org.apache.geronimo.naming.java.RootContext;
031    import org.apache.geronimo.security.Callers;
032    import org.apache.geronimo.security.ContextManager;
033    import org.apache.openejb.core.CoreDeploymentInfo;
034    import org.apache.openejb.core.ThreadContext;
035    import org.apache.openejb.core.ThreadContextListener;
036    
037    /**
038     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
039     */
040    public class GeronimoThreadContextListener implements ThreadContextListener {
041        private static final Log log = LogFactory.getLog(GeronimoThreadContextListener.class);
042    
043        // A single stateless listener is used for Geronimo
044        private static final GeronimoThreadContextListener instance = new GeronimoThreadContextListener();
045    
046        static {
047            ThreadContext.addThreadContextListener(instance);
048        }
049    
050        public static void init() {
051            // do nothing.. the goal here is to kick off the onetime init above
052        }
053    
054        private GeronimoThreadContextListener() {
055        }
056    
057        public void contextEntered(ThreadContext oldContext, ThreadContext newContext) {
058            CoreDeploymentInfo deploymentInfo = newContext.getDeploymentInfo();
059            if (deploymentInfo == null) return;
060            if (deploymentInfo.get(EjbDeployment.class) == null) {
061                synchronized (deploymentInfo) {
062                    if (deploymentInfo.get(EjbDeployment.class) == null) {
063                        if (!deploymentInfo.isDestroyed()) {
064                            try {
065                                deploymentInfo.wait();
066                            } catch (InterruptedException e) {
067                            log.warn("Wait on deploymentInfo interrupted unexpectedly");
068                            }
069                        }
070                    }
071                }
072            } 
073            EjbDeployment ejbDeployment = deploymentInfo.get(EjbDeployment.class);
074            if (ejbDeployment == null) return;
075    
076            // Geronimo call context is used to track old state that must be restored
077            GeronimoCallContext geronimoCallContext = new GeronimoCallContext();
078    
079            // Demarcate component boundaries for connection tracking if we have a tracker
080            TrackedConnectionAssociator trackedConnectionAssociator = ejbDeployment.getTrackedConnectionAssociator();
081            if (trackedConnectionAssociator != null) {
082                // create the connector context... this only works with a TrackedConnectionAssociator using lazy association
083                ConnectorInstanceContext connectorContext = new ConnectorInstanceContextImpl(ejbDeployment.getUnshareableResources(),
084                        ejbDeployment.getApplicationManagedSecurityResources());
085    
086                // Set connector context
087                try {
088                    geronimoCallContext.oldConnectorContext = trackedConnectionAssociator.enter(connectorContext);
089                } catch (ResourceException e) {
090                    log.error("Error while entering TrackedConnectionAssociator");
091                    return;
092                }
093            }
094    
095            // Get the jndi context
096            Context jndiContext = ejbDeployment.getComponentContext();
097            geronimoCallContext.oldJndiContext = RootContext.getComponentContext();
098            // Set the jndi context into Geronimo's root context
099            RootContext.setComponentContext(jndiContext);
100    
101            // set the policy (security) context id
102            geronimoCallContext.contextID = PolicyContext.getContextID();
103            String moduleID = newContext.getDeploymentInfo().getModuleID();
104            PolicyContext.setContextID(moduleID);
105    
106            // set the default subject if needed
107            if (ContextManager.getCurrentCaller() == null) {
108                Subject defaultSubject = ejbDeployment.getDefaultSubject();
109    
110                if (defaultSubject != null) {
111                    ContextManager.setCallers(defaultSubject, defaultSubject);
112                    geronimoCallContext.clearCallers = true;
113                }
114            }
115    
116            // apply run as
117            Subject runAsSubject = ejbDeployment.getRunAs();
118            geronimoCallContext.callers = ContextManager.pushNextCaller(runAsSubject);
119    
120            newContext.set(GeronimoCallContext.class, geronimoCallContext);
121        }
122    
123        public void contextExited(ThreadContext exitedContext, ThreadContext reenteredContext) {
124            CoreDeploymentInfo deploymentInfo = exitedContext.getDeploymentInfo();
125            if (deploymentInfo == null) return;
126    
127            EjbDeployment ejbDeployment = deploymentInfo.get(EjbDeployment.class);
128            if (ejbDeployment == null) return;
129    
130            // Geronimo call context is used to track old state that must be restored
131            GeronimoCallContext geronimoCallContext = exitedContext.get(GeronimoCallContext.class);
132            if (geronimoCallContext == null) return;
133    
134            // reset run as
135            ContextManager.popCallers(geronimoCallContext.callers);
136    
137            // reset default subject
138            if (geronimoCallContext.clearCallers) {
139                ContextManager.clearCallers();
140            }
141    
142            //reset ContextID
143            PolicyContext.setContextID(geronimoCallContext.contextID);
144    
145            // reset Geronimo's root jndi context
146            RootContext.setComponentContext(geronimoCallContext.oldJndiContext);
147    
148            // reset old connector context
149            TrackedConnectionAssociator trackedConnectionAssociator = ejbDeployment.getTrackedConnectionAssociator();
150            if (trackedConnectionAssociator != null) {
151                try {
152                    trackedConnectionAssociator.exit(geronimoCallContext.oldConnectorContext);
153                } catch (ResourceException e) {
154                    log.error("Error while exiting TrackedConnectionAssociator");
155                }
156            }
157        }
158    
159        private static final class GeronimoCallContext {
160            private Context oldJndiContext;
161            private ConnectorInstanceContext oldConnectorContext;
162            private boolean clearCallers;
163            private Callers callers;
164            private String contextID;
165        }
166    }