/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.tomee.catalina; import org.apache.naming.ContextBindings; import org.apache.openejb.core.ThreadContext; import org.apache.openejb.core.ThreadContextListener; import org.apache.openejb.util.LogCategory; import org.apache.openejb.util.Logger; import javax.naming.NamingException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Hashtable; /** * Tomcat thread context listener. * * @version $Rev$ $Date$ */ public class TomcatThreadContextListener implements ThreadContextListener { /** * Logger instance for tomcat */ private static final Logger logger = Logger.getInstance(LogCategory.OPENEJB.createChild("tomcat"), "org.apache.openejb.util.resources"); /** * OpenEJB context name */ private static final String OPENEJB_CONTEXT = "OpenEJBContext"; /** * getThreadName method in class ContextBindings */ protected Method method; private Hashtable<Thread, Object> threadNameBindings; /** * Creates a new instance. */ public TomcatThreadContextListener() { ContextBindings.bindContext(OPENEJB_CONTEXT, new OpenEJBContext()); try { // someone decided to make the getThreadName package protected so we have to use reflection method = ContextBindings.class.getDeclaredMethod("getThreadName"); method.setAccessible(true); final Field threadNameBindingsField = ContextBindings.class.getDeclaredField("threadObjectBindings"); threadNameBindingsField.setAccessible(true); threadNameBindings = (Hashtable<Thread, Object>) threadNameBindingsField.get(null); } catch (final Exception e) { logger.error("Expected ContextBinding to have the method getThreadName()"); } } /** * {@inheritDoc} */ public void contextEntered(final ThreadContext oldContext, final ThreadContext newContext) { // save off the old context if possible try { final Data data = new Data(getThreadName()); newContext.set(Data.class, data); } catch (final NamingException ignored) { // no-op } // set the new context try { ContextBindings.bindThread(OPENEJB_CONTEXT, null); } catch (final NamingException e) { ContextBindings.unbindContext(OPENEJB_CONTEXT, null); throw new IllegalArgumentException("Unable to bind OpenEJB enc"); } } /** * {@inheritDoc} */ public void contextExited(final ThreadContext exitedContext, final ThreadContext reenteredContext) { // unbind the new context ContextBindings.unbindThread(OPENEJB_CONTEXT, null); // attempt to restore the old context final Data data = exitedContext.get(Data.class); if (data != null && data.oldContextName != null) { try { ContextBindings.bindThread(data.oldContextName, null); } catch (final NamingException e) { logger.error("Exception in method contextExited", e); } } } /** * Gets thread name. * * @return thread name * @throws NamingException for exception */ private Object getThreadName() throws NamingException { try { return threadNameBindings.get(Thread.currentThread()); } catch (final Exception e) { // no-op: try the old implementation } // this implementation is probably better but slower try { return method.invoke(null); } catch (final InvocationTargetException e) { // if it's a naming exception, it should be treated by the caller if (e.getCause() != null && e.getCause() instanceof NamingException) { throw (NamingException) e.getCause(); } logger.error("Exception in method getThreadName", e); return null; } catch (final Exception e) { logger.error("Exception in method getThreadName", e); return null; } } //Internal stuff to hold old context name private static class Data { private Object oldContextName; public Data(final Object oldContextName) { this.oldContextName = oldContextName; } } }