001package org.kohsuke.stapler;
002
003import org.kohsuke.MetaInfServices;
004import org.kohsuke.stapler.export.ModelBuilder;
005import org.kohsuke.stapler.lang.Klass;
006import org.zeroturnaround.javarebel.ClassEventListener;
007import org.zeroturnaround.javarebel.ReloaderFactory;
008
009import javax.servlet.RequestDispatcher;
010import java.util.HashMap;
011import java.util.List;
012import java.util.Map;
013import java.util.Map.Entry;
014import java.util.logging.Logger;
015
016import static java.util.logging.Level.*;
017
018/**
019 * Adds JRebel reloading support.
020 *
021 * @author Kohsuke Kawaguchi
022 */
023@MetaInfServices
024public class JRebelFacet extends Facet {
025    private Map<Class,MetaClass> metaClasses;
026
027    public class ReloaderHook implements Runnable {
028        public void run() {
029            ReloaderFactory.getInstance().addClassReloadListener(new ClassEventListener() {
030                public void onClassEvent(int eventType, Class klass) {
031                    synchronized (metaClasses) {
032                        for (Entry<Class, MetaClass> e : metaClasses.entrySet()) {
033                            if (klass.isAssignableFrom(e.getKey())) {
034                                LOGGER.fine("Reloaded Stapler MetaClass for "+e.getKey());
035                                e.getValue().buildDispatchers();
036                            }
037                        }
038                    }
039                    // purge the model builder cache
040                    ResponseImpl.MODEL_BUILDER = new ModelBuilder();
041                }
042
043                public int priority() {
044                    return PRIORITY_DEFAULT;
045                }
046            });
047            metaClasses = new HashMap<Class, MetaClass>();
048        }
049    }
050
051    public JRebelFacet() {
052        try {
053            Runnable r = (Runnable)Class.forName(JRebelFacet.class.getName()+"$ReloaderHook")
054                    .getConstructor(JRebelFacet.class).newInstance(this);
055            r.run();
056        } catch (Throwable e) {
057            LOGGER.log(FINE, "JRebel support failed to load", e);
058        }
059    }
060
061
062    @Override
063    public void buildViewDispatchers(MetaClass owner, List<Dispatcher> dispatchers) {
064        if (metaClasses != null && owner.klass.clazz instanceof Class) {
065            synchronized (metaClasses) {
066                metaClasses.put((Class)owner.klass.clazz,owner);
067            }
068        }
069    }
070
071    @Override
072    public RequestDispatcher createRequestDispatcher(RequestImpl request, Klass<?> type, Object it, String viewName) {
073        return null;
074    }
075
076    @Override
077    public void buildIndexDispatchers(MetaClass owner, List<Dispatcher> dispatchers) {
078        // no-op
079    }
080
081    @Override
082    public boolean handleIndexRequest(RequestImpl req, ResponseImpl rsp, Object node, MetaClass nodeMetaClass) {
083        return false;
084    }
085
086    private static final Logger LOGGER = Logger.getLogger(JRebelFacet.class.getName());
087}