package er.wojrebel; import java.util.Enumeration; import org.zeroturnaround.javarebel.ClassEventListener; import org.zeroturnaround.javarebel.Logger; import org.zeroturnaround.javarebel.LoggerFactory; import org.zeroturnaround.javarebel.Reloader; import org.zeroturnaround.javarebel.ReloaderFactory; import com.webobjects.appserver.WOAction; import com.webobjects.appserver.WOApplication; import com.webobjects.appserver.WOComponent; import com.webobjects.appserver.WORequest; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation.NSKeyValueCoding; import com.webobjects.foundation.NSNotification; import com.webobjects.foundation.NSValidation; /** * WOJRebelClassReloadHandler manages the clearing of KVC, component definition and class caches * when a class is reloaded by JRebel. Any cached ClassNotFound entries are also removed. * * @author qdolan * */ public class WOJRebelClassReloadHandler { private static boolean initialized = false; private boolean resetKVCCaches = false; private boolean resetComponentCache = false; private boolean resetActionClassCache = false; private boolean resetValidationCache = false; private static final WOJRebelClassReloadHandler instance = new WOJRebelClassReloadHandler(); private static final Logger log = LoggerFactory.getInstance(); private WOJRebelClassReloadHandler() { /* Private */ } public static WOJRebelClassReloadHandler getInstance() { return instance; } private void doReset() { if (resetKVCCaches) { resetKVCCaches = false; log.echo("JRebel: Resetting KeyValueCoding caches"); NSKeyValueCoding.DefaultImplementation._flushCaches(); NSKeyValueCoding._ReflectionKeyBindingCreation._flushCaches(); NSKeyValueCoding.ValueAccessor._flushCaches(); } if (resetComponentCache) { resetComponentCache = false; log.echo("JRebel: Resetting Component Definition cache"); WOApplication.application()._removeComponentDefinitionCacheContents(); } if (resetActionClassCache) { resetActionClassCache = false; log.echo("JRebel: Resetting Action class cache"); WOClassCacheAccessor.clearActionClassCache(); } if (resetValidationCache) { resetValidationCache = false; log.echo("JRebel: Resetting NSValidation cache"); NSValidation.DefaultImplementation._flushCaches(); } } public void initialize() { if (initialized) { return; } initialized = true; if (WOApplication.application() != null && WOApplication.application().isCachingEnabled()) { System.out.println("Running in deployment mode. Rapid turnaround is disabled"); return; } if (!isReloadEnabled()) { System.out.println("JRebel rapid turnaround mode is disabled because JRebel is not running " + "\n To use JRebel rapid turnaround you must add the following to your " + "Java VM arguments:\n -noverify -javaagent:<pathtojar>/jrebel.jar"); System.out.println(" JRebel can be obtained from www.zeroturnaround.com"); return; } log.echo("JRebel: WebObjects support enabled"); WOEventClassListener listener = new WOEventClassListener(); Reloader reloader = ReloaderFactory.getInstance(); reloader.addClassReloadListener(listener); } @SuppressWarnings("all") public void reloaded(Class clazz) { resetKVCCaches = true; if (WOComponent.class.isAssignableFrom(clazz)) { resetComponentCache = true; } if (WOAction.class.isAssignableFrom(clazz)) { resetActionClassCache = true; } if (NSValidation.class.isAssignableFrom(clazz)) { resetValidationCache = true; } doReset(); } @SuppressWarnings("unchecked") public synchronized void updateLoadedClasses(NSNotification notification) { Reloader reloader = ReloaderFactory.getInstance(); if (notification != null) { WORequest request = (WORequest) notification.object(); String key = "/" + WOApplication.application().resourceRequestHandlerKey(); if (request.uri().indexOf(request.adaptorPrefix()) != 0 || request.uri().indexOf(key) >= 0) { return; } } NSDictionary classList = WOClassCacheAccessor.getClassCache(); String unknownClassName = "com.webobjects.foundation._NSUtilities$_NoClassUnderTheSun"; Class<?> unknownClass = WOClassCacheAccessor.classForName(unknownClassName); Enumeration<String> en = classList.keyEnumerator(); while(en.hasMoreElements()) { String className = en.nextElement(); if (className.equals(unknownClassName)) { continue; } Class<?> clazz = WOClassCacheAccessor.classForName(className); if (clazz != null && clazz.isPrimitive()) { continue; } if (clazz == null || clazz.equals(unknownClass)) { WOClassCacheAccessor.removeClassForName(className); continue; } reloader.checkAndReload(clazz); } doReset(); } public boolean isReloadEnabled() { return ReloaderFactory.getInstance().isReloadEnabled(); } private class WOEventClassListener implements ClassEventListener { public void onClassEvent(int eventType, Class clazz) { if (eventType == ClassEventListener.EVENT_RELOADED) { reloaded(clazz); } } public int priority() { return 0; } } }