001/*
002 * Shredzone Commons
003 *
004 * Copyright (C) 2012 Richard "Shred" Körber
005 *   http://commons.shredzone.org
006 *
007 * This program is free software: you can redistribute it and/or modify
008 * it under the terms of the GNU Library General Public License as
009 * published by the Free Software Foundation, either version 3 of the
010 * License, or (at your option) any later version.
011 *
012 * This program is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015 * GNU General Public License for more details.
016 *
017 * You should have received a copy of the GNU Library General Public License
018 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
019 */
020package org.shredzone.commons.taglib.proxy;
021
022import java.util.Enumeration;
023
024import javax.annotation.Nonnull;
025import javax.servlet.jsp.JspContext;
026import javax.servlet.jsp.PageContext;
027import javax.servlet.jsp.tagext.JspTag;
028
029import org.springframework.beans.factory.BeanFactory;
030import org.springframework.web.servlet.FrameworkServlet;
031
032/**
033 * An abstract TagProxy implementation that offers all basic methods.
034 *
035 * @param <T>
036 *            Type of the {@link JspTag} this proxy delegates to
037 * @author Richard "Shred" Körber
038 */
039public abstract class AbstractTagProxy<T extends JspTag> implements JspTag, ProxiedTag<T> {
040
041    public static final String TAGPROXY_BEANFACTORY_CACHE = AbstractTagProxy.class + ".beanFactory";
042
043    private T tagImpl;
044
045    protected abstract String getBeanName();
046
047    /**
048     * Creates a new instance of the implementing target bean.
049     *
050     * @param jspContext
051     *            {@link JspContext}
052     */
053    @SuppressWarnings("unchecked")
054    protected void initTargetBean(@Nonnull JspContext jspContext) {
055        BeanFactory bf = getBeanFactory(jspContext);
056
057        String beanName = getBeanName();
058        if (!bf.isPrototype(beanName)) {
059            throw new IllegalStateException("Bean " + beanName + " must be prototype scoped!");
060        }
061
062        tagImpl = (T) bf.getBean(beanName);
063    }
064
065    /**
066     * Gets the {@link BeanFactory} from the given {@link JspContext}. The default
067     * implementation automagically finds a {@link BeanFactory} that was previously set by
068     * a {@link FrameworkServlet}. The result is cached.
069     *
070     * @param jspContext
071     *            {@link JspContext} to be used
072     * @return {@link BeanFactory} found
073     */
074    @SuppressWarnings("unchecked")
075    protected @Nonnull BeanFactory getBeanFactory(@Nonnull JspContext jspContext) {
076        Object bfCache = jspContext.getAttribute(TAGPROXY_BEANFACTORY_CACHE, PageContext.APPLICATION_SCOPE);
077        if (bfCache != null && bfCache instanceof BeanFactory) {
078            return (BeanFactory) bfCache;
079        }
080
081        Enumeration<String> en = jspContext.getAttributeNamesInScope(PageContext.APPLICATION_SCOPE);
082        while (en.hasMoreElements()) {
083            String attribute = en.nextElement();
084            if (attribute.startsWith(FrameworkServlet.SERVLET_CONTEXT_PREFIX)) {
085                Object bf = jspContext.getAttribute(attribute, PageContext.APPLICATION_SCOPE);
086                if (bf != null && bf instanceof BeanFactory) {
087                    BeanFactory bfBean = (BeanFactory) bf;
088                    jspContext.setAttribute(TAGPROXY_BEANFACTORY_CACHE, bfBean, PageContext.APPLICATION_SCOPE);
089                    return bfBean;
090                }
091            }
092        }
093
094        throw new IllegalStateException("Could not find a BeanFactory. Use a FrameworkServlet or @BeanFactoryReference.");
095    }
096
097    @Override
098    public T getTargetBean() {
099        return tagImpl;
100    }
101
102}