001/*
002 * Copyright (c) 2004-2010, Kohsuke Kawaguchi
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification, are permitted provided
006 * that the following conditions are met:
007 *
008 *     * Redistributions of source code must retain the above copyright notice, this list of
009 *       conditions and the following disclaimer.
010 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
011 *       conditions and the following disclaimer in the documentation and/or other materials
012 *       provided with the distribution.
013 *
014 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
015 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
016 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
017 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
018 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
019 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
020 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
021 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
022 */
023
024package org.kohsuke.stapler.jelly;
025
026import org.apache.commons.jelly.JellyException;
027import org.apache.commons.jelly.Script;
028import org.kohsuke.stapler.AbstractTearOff;
029import static org.kohsuke.stapler.Dispatcher.trace;
030import static org.kohsuke.stapler.Dispatcher.traceable;
031
032import org.kohsuke.stapler.Dispatcher;
033import org.kohsuke.stapler.Facet;
034import org.kohsuke.stapler.MetaClass;
035import org.kohsuke.stapler.StaplerRequest;
036import org.kohsuke.stapler.StaplerResponse;
037import org.kohsuke.stapler.WebApp;
038
039import javax.servlet.RequestDispatcher;
040import javax.servlet.ServletException;
041import java.io.IOException;
042import java.net.URL;
043import java.util.logging.Logger;
044
045/**
046 * @author Kohsuke Kawaguchi
047 */
048public class JellyClassTearOff extends AbstractTearOff<JellyClassLoaderTearOff,Script,JellyException> {
049    private JellyFacet facet;
050
051    public JellyClassTearOff(MetaClass owner) {
052        super(owner,JellyClassLoaderTearOff.class);
053        facet = owner.webApp.getFacet(JellyFacet.class);
054    }
055
056    protected Script parseScript(URL res) throws JellyException {
057        return new JellyViewScript(owner.klass, res, classLoader.createContext().compileScript(res));
058    }
059
060    @Override
061    protected String getDefaultScriptExtension() {
062        return ".jelly";
063    }
064
065    @Override
066    protected boolean hasAllowedExtension(String name) {
067        return name.endsWith(".jelly") || name.endsWith(".jellytag");
068    }
069
070    /**
071     * Aside from looking into our own, consult other facets that can handle Jelly-compatible scripts.
072     */
073    @Override
074    public Script resolveScript(String name) throws JellyException {
075        // cut off the extension so that we can search all the extensions
076        String shortName;
077        int dot = name.lastIndexOf('.');
078        if (dot>name.lastIndexOf('/'))      shortName = name.substring(0, dot);
079        else                                shortName = name;
080
081        for (Facet f : owner.webApp.facets) {
082            if (f instanceof JellyCompatibleFacet && !(f instanceof JellyFacet)) {
083                JellyCompatibleFacet jcf = (JellyCompatibleFacet) f;
084                for (Class<? extends AbstractTearOff<?,? extends Script,?>> ct : jcf.getClassTearOffTypes()) {
085                    try {
086                        Script s = owner.loadTearOff(ct).resolveScript(shortName);
087                        if (s!=null)    return s;
088                    } catch (Exception e) {
089                        throw new JellyException("Failed to load "+shortName+" from "+jcf,e);
090                    }
091                }
092            }
093        }
094
095        return super.resolveScript(name);
096    }
097
098    /**
099     * Serves <tt>index.jelly</tt> if it's available, and returns true.
100     */
101    public boolean serveIndexJelly(StaplerRequest req, StaplerResponse rsp, Object node) throws ServletException, IOException {
102        try {
103            Script script = findScript("index.jelly");
104            if(script!=null) {
105                String src = "index.jelly";
106                if (script instanceof JellyViewScript) {
107                    JellyViewScript jvs = (JellyViewScript) script;
108                    src = jvs.getName();
109                }
110                Dispatcher.anonymizedTraceEval(req, rsp, node, "%s: Jelly index: %s", src);
111                if(traceable()) {
112                    trace(req,rsp,"-> %s on <%s>",src,node);
113                }
114                facet.scriptInvoker.invokeScript(req, rsp, script, node);
115                return true;
116            }
117            return false;
118        } catch (JellyException e) {
119            throw new ServletException(e);
120        }
121    }
122
123    /**
124     * Creates a {@link RequestDispatcher} that forwards to the jelly view, if available.
125     */
126    public RequestDispatcher createDispatcher(Object it, String viewName) throws IOException {
127        try {
128            // backward compatible behavior that expects full file name including ".jelly"
129            Script script = findScript(viewName);
130            if(script!=null)
131                return new JellyRequestDispatcher(it,script);
132            
133            // this is what the look up was really supposed to be.
134            script = findScript(viewName+".jelly");
135            if(script!=null)
136                return new JellyRequestDispatcher(it,script);
137            return null;
138        } catch (JellyException e) {
139            IOException io = new IOException(e.getMessage());
140            io.initCause(e);
141            throw io;
142        }
143    }
144
145    private static final Logger LOGGER = Logger.getLogger(JellyClassTearOff.class.getName());
146}