/** * Copyright (C) 2010 hprange <hprange@gmail.com> * * Licensed 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 com.woinject.servlet; import java.lang.reflect.Method; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import com.woinject.WOInject; /** * <p> * The <code>WOInjectServletContextListener</code> initializes the WOInject * framework in the context of servlet applications. It's important to * initialize WOInject very early in the context of a servlet application. * Remember to add the listener in the first place on the web.xml descriptor. * </p> * <p> * Add the following snippet into the web.xml: * </p> * * <pre> * <listener> * <listener-class>com.woinject.servlet.WOInjectServletContextListener</listener-class> * </listener> * </pre> * * @author <a href="mailto:hprange@gmail.com.br">Henrique Prange</a> * @since 1.2 */ public class WOInjectServletContextListener implements ServletContextListener { private static final String WOINJECT_MARKER_ATTRIBUTE = WOInject.class.getName(); private static Class<?> loadClass(ClassLoader loader, String classname, byte[] bytes) { Class<?> clazz = null; try { Class<?> loaderClass = Class.forName("java.lang.ClassLoader"); Method method = loaderClass.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, int.class, int.class }); method.setAccessible(true); try { Object[] args = new Object[] { classname, bytes, 0, bytes.length }; clazz = (Class<?>) method.invoke(loader, args); } finally { method.setAccessible(false); } } catch (Exception exception) { throw new Error("Error while defining class.", exception); } return clazz; } /** * Remove the WOInject marker associated with this context. * * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent) */ public void contextDestroyed(ServletContextEvent event) { ServletContext servletContext = event.getServletContext(); servletContext.removeAttribute(WOINJECT_MARKER_ATTRIBUTE); } /** * Load the changed <code>_NSUtilities</code> class required by WOInject * during the context initialization. * * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent) */ public void contextInitialized(ServletContextEvent event) { ServletContext context = event.getServletContext(); if (context.getAttribute(WOINJECT_MARKER_ATTRIBUTE) != null) { // WOInject has already been initialized in this context return; } ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(WOInjectServletContextListener.class)); try { String classname = "com.webobjects.foundation._NSUtilities"; CtClass clazz = pool.get(classname); CtMethod method = clazz.getDeclaredMethod("instantiateObject"); method.insertBefore("{ return com.webobjects.foundation.InstantiationInterceptor.instantiateObject($1, $2, $3, $4, $5); }"); method = clazz.getDeclaredMethod("instantiateObjectWithConstructor"); method.insertBefore("{ return com.webobjects.foundation.InstantiationInterceptor.instantiateObject($1, $2, $3, $4, $5); }"); ClassLoader loader = Thread.currentThread().getContextClassLoader(); loadClass(loader, classname, clazz.toBytecode()); } catch (Throwable exception) { throw new Error("Cannot initialize the application to take advantage of WOInject features.", exception); } context.setAttribute(WOINJECT_MARKER_ATTRIBUTE, new Object()); } }