Package org.apache.geronimo.jasper.deployment

Source Code of org.apache.geronimo.jasper.deployment.JspModuleBuilderExtension

/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.geronimo.jasper.deployment;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarFile;

import org.apache.geronimo.common.DeploymentException;
import org.apache.geronimo.deployment.Deployable;
import org.apache.geronimo.deployment.DeployableBundle;
import org.apache.geronimo.deployment.DeployableJarFile;
import org.apache.geronimo.deployment.ModuleIDBuilder;
import org.apache.geronimo.deployment.service.EnvironmentBuilder;
import org.apache.geronimo.gbean.AbstractName;
import org.apache.geronimo.gbean.GBeanData;
import org.apache.geronimo.gbean.GBeanInfo;
import org.apache.geronimo.gbean.GBeanInfoBuilder;
import org.apache.geronimo.gbean.annotation.GBean;
import org.apache.geronimo.gbean.annotation.ParamAttribute;
import org.apache.geronimo.gbean.annotation.ParamReference;
import org.apache.geronimo.j2ee.annotation.Holder;
import org.apache.geronimo.j2ee.deployment.EARContext;
import org.apache.geronimo.j2ee.deployment.Module;
import org.apache.geronimo.j2ee.deployment.ModuleBuilderExtension;
import org.apache.geronimo.j2ee.deployment.NamingBuilder;
import org.apache.geronimo.j2ee.deployment.WebModule;
import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
import org.apache.geronimo.jasper.JasperServletContextCustomizer;
import org.apache.geronimo.jasper.TldProvider;
import org.apache.geronimo.jasper.TldRegistry;
import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
import org.apache.geronimo.kernel.Naming;
import org.apache.geronimo.kernel.config.ConfigurationStore;
import org.apache.geronimo.kernel.repository.Environment;
import org.apache.geronimo.kernel.util.IOUtils;
import org.apache.geronimo.web.info.ServletInfo;
import org.apache.geronimo.web.info.WebAppInfo;
import org.apache.geronimo.web25.deployment.AbstractWebModuleBuilder;
import org.apache.geronimo.web25.deployment.JspServletInfoProvider;
import org.apache.geronimo.web25.deployment.WebAppInfoBuilder;
import org.apache.openejb.jee.JaxbJavaee;
import org.apache.openejb.jee.JspConfig;
import org.apache.openejb.jee.JspPropertyGroup;
import org.apache.openejb.jee.Listener;
import org.apache.openejb.jee.Tag;
import org.apache.openejb.jee.Taglib;
import org.apache.openejb.jee.TldTaglib;
import org.apache.openejb.jee.WebApp;
import org.apache.xbean.finder.AbstractFinder;
import org.apache.xbean.finder.ClassFinder;
import org.apache.xmlbeans.XmlObject;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* This JSP module builder extension is meant to find all the TLD descriptor files associated with a
* deployable artifact, search those TLD files for listeners, search those listeners for
* annotations, and ultimately create a ClassFinder using those annoated classes (for later
* processing by the various naming builders)
*
* @version $Rev $Date
*/
@GBean(j2eeType = NameFactory.MODULE_BUILDER)
public class JspModuleBuilderExtension implements ModuleBuilderExtension, JspServletInfoProvider {

    private static final Logger log = LoggerFactory.getLogger(JspModuleBuilderExtension.class);

    private final Environment defaultEnvironment;
    private final NamingBuilder namingBuilders;
    private final Set<String> excludedListenerNames = new HashSet<String>();
    private final ServletInfo defaultJspServletInfo;

    public JspModuleBuilderExtension(@ParamAttribute(name = "defaultEnvironment") Environment defaultEnvironment,
                                     @ParamAttribute(name = "excludedListenerNames") Collection<String> excludedListenerNames,
                                     @ParamAttribute(name = "defaultJspServlet") WebAppInfo defaultJspServlet,
                                     @ParamReference(name = "NamingBuilders", namingType = NameFactory.MODULE_BUILDER) NamingBuilder namingBuilders) {
        this.defaultEnvironment = defaultEnvironment;
        this.namingBuilders = namingBuilders;
        if (excludedListenerNames != null) {
            this.excludedListenerNames.addAll(excludedListenerNames);
        }
        if (defaultJspServlet == null || defaultJspServlet.servlets.size() != 1) {
            throw new IllegalArgumentException("Must supply exactly one default jsp servlet");
        }
        defaultJspServletInfo = defaultJspServlet.servlets.get(0);
    }

    public void createModule(Module module, Bundle bundle, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
    }

    public void createModule(Module module, Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, Environment environment, Object moduleContextInfo, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
    }

    public void installModule(JarFile earFile, EARContext earContext, Module module, Collection configurationStores, ConfigurationStore targetConfigurationStore, Collection repository) throws DeploymentException {
    }

    public void initContext(EARContext earContext, Module module, Bundle bundle) throws DeploymentException {
    }

    public void addGBeans(EARContext earContext, Module module, Bundle bundle, Collection repository) throws DeploymentException {
        if (!(module instanceof WebModule)) {
            //not a web module, nothing to do
            return;
        }
        //TODO Only merge if we detect jsps???
        EnvironmentBuilder.mergeEnvironments(module.getEnvironment(), defaultEnvironment);

        WebModule webModule = (WebModule) module;
        WebApp webApp = webModule.getSpecDD();

        EARContext moduleContext = module.getEarContext();
        Map sharedContext = module.getSharedContext();

        GBeanData webAppData = (GBeanData) sharedContext.get(WebModule.WEB_APP_DATA);

        AbstractName moduleName = module.getModuleName();
        Map<EARContext.Key, Object> buildingContext = new HashMap<EARContext.Key, Object>();
        buildingContext.put(NamingBuilder.GBEAN_NAME_KEY, moduleName);


        //use the same holder object as the web app.
        Holder holder = NamingBuilder.INJECTION_KEY.get(sharedContext);
        buildingContext.put(NamingBuilder.INJECTION_KEY, holder);

        XmlObject jettyWebApp = webModule.getVendorDD();

        Set<String> listenerNames = new HashSet<String>();

        Map<String, Bundle> tldLocationBundleMap = getTldFiles(webApp, webModule);
        LinkedHashSet<Class<?>> classes = getListenerClasses(webApp, webModule, tldLocationBundleMap, listenerNames);
       
        AbstractFinder originalClassFinder = webModule.getClassFinder();
        ClassFinder classFinder = new ClassFinder(new ArrayList<Class<?>>(classes));
        webModule.setClassFinder(classFinder);
        namingBuilders.buildNaming(webApp, jettyWebApp, webModule, buildingContext);
        webModule.setClassFinder(originalClassFinder);

        //only try to install it if reference will work.
        //Some users (tomcat?) may have back doors into jasper that make adding this gbean unnecessary.
        GBeanInfo webAppGBeanInfo = webAppData.getGBeanInfo();
        if (webAppGBeanInfo.getReference("ContextCustomizer") != null) {
            AbstractName jspLifecycleName = moduleContext.getNaming().createChildName(moduleName, "jspLifecycleProvider", GBeanInfoBuilder.DEFAULT_J2EE_TYPE);
            GBeanData gbeanData = new GBeanData(jspLifecycleName, JasperServletContextCustomizer.class);
            gbeanData.setAttribute("holder", holder);

            try {
                moduleContext.addGBean(gbeanData);
            } catch (GBeanAlreadyExistsException e) {
                throw new DeploymentException("Duplicate jspLifecycleProvider", e);
            }

            webAppData.setReferencePattern("ContextCustomizer", jspLifecycleName);
        }

        WebAppInfoBuilder webAppInfoBuilder = (WebAppInfoBuilder)sharedContext.get(WebModule.WEB_APP_INFO);
        if (webAppInfoBuilder != null) {
            WebAppInfo webAppInfo = webAppInfoBuilder.getWebAppInfo();
            webAppInfo.listeners.addAll(listenerNames);
            //install default jsp servlet....
            ServletInfo jspServlet = webAppInfoBuilder.copy(defaultJspServletInfo);
            List<JspConfig> jspConfigs = webApp.getJspConfig();
            List<String> jspMappings = new ArrayList<String>();
            for (JspConfig jspConfig : jspConfigs) {
                for (JspPropertyGroup propertyGroup : jspConfig.getJspPropertyGroup()) {
                    WebAppInfoBuilder.normalizeUrlPatterns(propertyGroup.getUrlPattern(), jspMappings);
                }
            }

            jspServlet.servletMappings.addAll(jspMappings);
            for (ServletInfo servletInfo: webAppInfo.servlets) {
                servletInfo.servletMappings.removeAll(jspMappings);
            }
            webAppInfo.servlets.add(jspServlet);
        } else {
            GBeanData jspServletData = AbstractWebModuleBuilder.DEFAULT_JSP_SERVLET_KEY.get(sharedContext);
            if (jspServletData != null) {
                try {
                    moduleContext.addGBean(jspServletData);
                } catch (GBeanAlreadyExistsException e) {
                    throw new DeploymentException("jsp servlet already present", e);
                }
            }
            // add listeners
            Object value = webAppData.getAttribute("listenerClassNames");
            if (value instanceof Collection) {
                ((Collection<String>) value).addAll(listenerNames);
            }
        }
    }

   
    @Override
    public ServletInfo getJspServletInfo() {
        return defaultJspServletInfo;
    }

    /**
     * getTldFiles(): Find all the TLD files in the web module being deployed
     * <p/>
     * <p>Locations to search for these TLD file(s) (matches the precedence search order for TLD
     * files per the JSP specs):
     * <ol>
     * <li>web.xml <taglib> entries
     * <li>TLD(s) in JAR files in WEB-INF/lib
     * <li>TLD(s) under WEB-INF
     * <li>All TLD files in all META-INF(s)
     * </ol>
     *
     * @param webApp    spec DD for module
     * @param webModule module being deployed
     * @return list of the URL(s) for the TLD files
     * @throws DeploymentException if there's a problem finding a tld file
     */
    private Map<String, Bundle> getTldFiles(WebApp webApp, WebModule webModule) throws DeploymentException {
        if (log.isDebugEnabled()) {
            log.debug("getTldFiles( " + webApp.toString() + "," + webModule.getName() + " ): Entry");
        }

        Map<String, Bundle> tldLocationBundleMap = new LinkedHashMap<String, Bundle>();
        Bundle webBundle = webModule.getEarContext().getDeploymentBundle();

        // 1. web.xml <taglib> entries
        List<JspConfig> jspConfigs = webApp.getJspConfig();
        for (JspConfig jspConfig : jspConfigs) {
            List<Taglib> taglibs = jspConfig.getTaglib();
            for (Taglib taglib : taglibs) {
                String uri = taglib.getTaglibUri().trim();
                String location = taglib.getTaglibLocation().trim();
                if (!location.equals("")) {
                    if (location.startsWith("/")) {
                        location = location.substring(1);
                    }
                    try {
                        URL targetUrl = webModule.getEarContext().getTargetURL(webModule.resolve(createURI(location)));
                        if (targetUrl != null) {
                            tldLocationBundleMap.put(targetUrl.toString(), webBundle);
                        }
                    } catch (URISyntaxException use) {
                        throw new DeploymentException("Could not locate TLD file specified in <taglib>: URI: " + uri + " Location: " + location + " " + use.getMessage(), use);
                    }
                }
            }
        }

        // 2. TLD(s) in JAR files in WEB-INF/lib
        // 3. TLD(s) under WEB-INF
        for (URL tldURL : scanModule(webModule)) {
            tldLocationBundleMap.put(tldURL.toString(), webBundle);
        }

        // 4. All TLD files in all META-INF(s)
        tldLocationBundleMap.putAll(scanGlobalTlds(webModule.getEarContext().getDeploymentBundle()));
        if (log.isDebugEnabled()) {
            log.debug("getTldFiles() Exit: URL[" + tldLocationBundleMap.size() + "]: " + tldLocationBundleMap.toString());
        }
        return tldLocationBundleMap;
    }

    /**
     * scanModule(): Scan the module being deployed for JAR files or TLD files in the WEB-INF
     * directory
     *
     * @param webModule module being deployed
     * @return list of the URL(s) for the TLD files in the module
     * @throws DeploymentException if module cannot be scanned
     */
    private List<URL> scanModule(WebModule webModule) throws DeploymentException {
        Deployable deployable = webModule.getDeployable();
        if (deployable instanceof DeployableJarFile) {
            JarFileTldScanner scanner = new JarFileTldScanner();
            return scanner.scanModule(webModule);
        } else if (deployable instanceof DeployableBundle) {
            BundleTldScanner scanner = new BundleTldScanner();
            return scanner.scanModule(webModule);
        }
        return Collections.emptyList();
    }

    private Map<String, Bundle> scanGlobalTlds(Bundle bundle) throws DeploymentException {
        BundleContext bundleContext = bundle.getBundleContext();
        ServiceReference reference = bundleContext.getServiceReference(TldRegistry.class.getName());
        Map<String, Bundle> tldLocationBundleMap = new HashMap<String, Bundle>();
        if (reference != null) {
            TldRegistry tldRegistry = (TldRegistry) bundleContext.getService(reference);
            for (TldProvider.TldEntry entry : tldRegistry.getDependentTlds(bundle)) {
                URL url = entry.getURL();
                tldLocationBundleMap.put(url.toString(), entry.getBundle());
            }
            bundleContext.ungetService(reference);
        }
        return tldLocationBundleMap;
    }

    private LinkedHashSet<Class<?>> getListenerClasses(WebApp webApp, WebModule webModule, Map<String, Bundle> tldLocationBundleMap, Set<String> listenerNames) throws DeploymentException {
        if (log.isDebugEnabled()) {
            log.debug("getListenerClasses( " + webApp.toString() + "," + '\n' +
                    webModule.getName() + " ): Entry");
        }

        // Get the classloader from the module's EARContext
        LinkedHashSet<Class<?>> classes = new LinkedHashSet<Class<?>>();

        try {
            for (Map.Entry<String, Bundle> entry : tldLocationBundleMap.entrySet()) {
                parseTldFile(new URL(entry.getKey()), entry.getValue(), classes, listenerNames);
            }
        } catch (MalformedURLException e) {
            throw new DeploymentException("Fail to parse the tld files", e);
        }

        if (log.isDebugEnabled()) {
            log.debug("getListenerClasses() Exit: Classes[" + classes.size() + "]: " + classes.toString());
        }
        return classes;
    }

    protected void parseTldFile(URL url, Bundle bundle, LinkedHashSet<Class<?>> classes, Set<String> listenerNames) throws DeploymentException {
        if (log.isDebugEnabled()) {
            log.debug("parseTLDFile( " + url.toString() + " ): Entry");
        }
        try {
            InputStream in = null;
            TldTaglib tl;
            try {
                in = url.openStream();
                tl = (TldTaglib) JaxbJavaee.unmarshalTaglib(TldTaglib.class, in);
            } finally {
                IOUtils.close(in);
            }

            // Get all the listeners from the TLD file
            for (Listener listener : tl.getListener()) {
                String className = listener.getListenerClass();
                if (!excludedListenerNames.contains(className)) {
                    try {
                        Class<?> clas = bundle.loadClass(className);
                        while (clas != null) {
                            classes.add(clas);
                            clas = clas.getSuperclass();
                        }
                        listenerNames.add(className);
                    } catch (ClassNotFoundException e) {
                        log.warn("JspModuleBuilderExtension: Could not load listener class: " + className + " mentioned in TLD file at " + url.toString());
                    }
                }
            }

            // Get all the tags from the TLD file
            for (Tag tag : tl.getTag()) {
                String className = tag.getTagClass();
                try {
                    Class<?> clas = bundle.loadClass(className);
                    while (clas != null) {
                        classes.add(clas);
                        clas = clas.getSuperclass();
                    }
                } catch (ClassNotFoundException e) {
                    log.warn("JspModuleBuilderExtension: Could not load tag class: " + className + " mentioned in TLD file at " + url.toString());
                }
            }
        } catch (Exception ioe) {
            throw new DeploymentException("Could not find TLD file at " + url.toString(), ioe);
        }
        if (log.isDebugEnabled()) {
            log.debug("parseTLDFile(): Exit");
        }
    }

    private URI createURI(String path) throws URISyntaxException {
        path = path.replaceAll(" ", "%20");
        return new URI(path);
    }
}
TOP

Related Classes of org.apache.geronimo.jasper.deployment.JspModuleBuilderExtension

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.