| // |
| // ======================================================================== |
| // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| package org.eclipse.jetty.osgi.boot.jasper; |
| |
| import java.io.File; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| import java.util.regex.Pattern; |
| |
| import javax.servlet.jsp.JspFactory; |
| |
| import org.eclipse.jetty.deploy.DeploymentManager; |
| import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator; |
| import org.eclipse.jetty.osgi.boot.OSGiWebInfConfiguration; |
| import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper; |
| import org.eclipse.jetty.osgi.boot.utils.TldBundleDiscoverer; |
| import org.eclipse.jetty.util.log.Log; |
| import org.eclipse.jetty.util.log.Logger; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.FrameworkUtil; |
| |
| |
| |
| /** |
| * ContainerTldBundleDiscoverer |
| * |
| * Finds bundles that are considered as on the container classpath that |
| * contain tlds. |
| * |
| * The System property org.eclipse.jetty.osgi.tldbundles is a comma |
| * separated list of exact symbolic names of bundles that have container classpath |
| * tlds. |
| * |
| * The DeploymentManager context attribute "org.eclipse.jetty.server.webapp.containerIncludeBundlePattern" |
| * can be used to define a pattern of symbolic names of bundles that contain container |
| * classpath tlds. |
| * |
| * The matching bundles are converted to URLs that are put onto a special classloader that acts as the |
| * parent classloader for contexts deployed by the jetty Server instance (see ServerInstanceWrapper). |
| * |
| * It also discovers the bundle that contains the jstl taglib and adds it into the |
| * "org.eclipse.jetty.server.webapp.containerIncludeBundlePattern" (if it is not already there) so |
| * that the WebInfOSGiConfiguration class will add the jstl taglib bundle into the list of container |
| * resources. |
| * |
| * Eg: |
| * -Dorg.eclipse.jetty.osgi.tldbundles=org.springframework.web.servlet,com.opensymphony.module.sitemesh |
| * |
| */ |
| public class ContainerTldBundleDiscoverer implements TldBundleDiscoverer |
| { |
| |
| private static final Logger LOG = Log.getLogger(ContainerTldBundleDiscoverer.class); |
| |
| |
| private static String DEFAULT_JSP_FACTORY_IMPL_CLASS = "org.apache.jasper.runtime.JspFactoryImpl"; |
| /** |
| * Default name of a class that belongs to the jstl bundle. From that class |
| * we locate the corresponding bundle and register it as a bundle that |
| * contains tld files. |
| */ |
| private static String DEFAULT_JSTL_BUNDLE_CLASS = "org.apache.taglibs.standard.tag.rt.core.WhenTag"; |
| |
| private Bundle jstlBundle = null; |
| |
| /** |
| * Check the System property "org.eclipse.jetty.osgi.tldbundles" for names of |
| * bundles that contain tlds and convert to URLs. |
| * |
| * @return The location of the jars that contain tld files as URLs. |
| */ |
| public URL[] getUrlsForBundlesWithTlds(DeploymentManager deploymentManager, BundleFileLocatorHelper locatorHelper) throws Exception |
| { |
| if (!isJspAvailable()) |
| { |
| return new URL[0]; |
| } |
| |
| if (jstlBundle == null) |
| jstlBundle = findJstlBundle(); |
| |
| Bundle[] bundles = FrameworkUtil.getBundle(ContainerTldBundleDiscoverer.class).getBundleContext().getBundles(); |
| HashSet<URL> urls = new HashSet<URL>(); |
| String tmp = System.getProperty(OSGiWebInfConfiguration.SYS_PROP_TLD_BUNDLES); //comma separated exact names |
| List<String> sysNames = new ArrayList<String>(); |
| if (tmp != null) |
| { |
| StringTokenizer tokenizer = new StringTokenizer(tmp, ", \n\r\t", false); |
| while (tokenizer.hasMoreTokens()) |
| sysNames.add(tokenizer.nextToken()); |
| } |
| tmp = (String) deploymentManager.getContextAttribute(OSGiWebInfConfiguration.CONTAINER_BUNDLE_PATTERN); //bundle name patterns |
| |
| Pattern pattern = (tmp==null? null : Pattern.compile(tmp)); |
| |
| //check that the jstl bundle is not already included in the pattern, and include it if it is not because |
| //subsequent classes such as OSGiWebInfConfiguration use this pattern to determine which jars are |
| //considered to be on the container classpath |
| if (jstlBundle != null) |
| { |
| if (pattern == null) |
| { |
| pattern = Pattern.compile(jstlBundle.getSymbolicName()); |
| deploymentManager.setContextAttribute(OSGiWebInfConfiguration.CONTAINER_BUNDLE_PATTERN, jstlBundle.getSymbolicName()); |
| } |
| else if (!(pattern.matcher(jstlBundle.getSymbolicName()).matches())) |
| { |
| String s = tmp+"|"+jstlBundle.getSymbolicName(); |
| pattern = Pattern.compile(s); |
| deploymentManager.setContextAttribute(OSGiWebInfConfiguration.CONTAINER_BUNDLE_PATTERN, s); |
| } |
| } |
| |
| |
| for (Bundle bundle : bundles) |
| { |
| if (sysNames.contains(bundle.getSymbolicName())) |
| convertBundleLocationToURL(locatorHelper, bundle, urls); |
| else if (pattern != null && pattern.matcher(bundle.getSymbolicName()).matches()) |
| convertBundleLocationToURL(locatorHelper, bundle, urls); |
| } |
| |
| return urls.toArray(new URL[urls.size()]); |
| |
| } |
| |
| /** |
| * Check that jsp is on the classpath |
| * @return |
| */ |
| public boolean isJspAvailable() |
| { |
| try |
| { |
| getClass().getClassLoader().loadClass("org.apache.jasper.servlet.JspServlet"); |
| } |
| catch (Exception e) |
| { |
| LOG.warn("Unable to locate the JspServlet: jsp support unavailable.", e); |
| return false; |
| } |
| return true; |
| } |
| |
| |
| /** |
| * |
| * Some versions of JspFactory do Class.forName, which probably won't work in an |
| * OSGi environment. |
| */ |
| public void fixJspFactory () |
| { |
| try |
| { |
| Class<javax.servlet.ServletContext> servletContextClass = javax.servlet.ServletContext.class; |
| // bug #299733 |
| JspFactory fact = JspFactory.getDefaultFactory(); |
| if (fact == null) |
| { // bug #299733 |
| // JspFactory does a simple |
| // Class.getForName("org.apache.jasper.runtime.JspFactoryImpl") |
| // however its bundles does not import the jasper package |
| // so it fails. let's help things out: |
| fact = (JspFactory) JettyBootstrapActivator.class.getClassLoader().loadClass(DEFAULT_JSP_FACTORY_IMPL_CLASS).newInstance(); |
| JspFactory.setDefaultFactory(fact); |
| } |
| } |
| catch (Exception e) |
| { |
| LOG.warn("Unable to set the JspFactory: jsp support incomplete.", e); |
| } |
| } |
| |
| |
| /** |
| * Find the bundle that contains a jstl implementation class, which assumes that |
| * the jstl taglibs will be inside the same bundle. |
| * @return |
| */ |
| public Bundle findJstlBundle () |
| { |
| Class<?> jstlClass = null; |
| |
| try |
| { |
| jstlClass = JSTLBundleDiscoverer.class.getClassLoader().loadClass(DEFAULT_JSTL_BUNDLE_CLASS); |
| } |
| catch (ClassNotFoundException e) |
| { |
| LOG.info("jstl not on classpath", e); |
| } |
| |
| if (jstlClass != null) |
| //get the bundle containing jstl |
| return FrameworkUtil.getBundle(jstlClass); |
| |
| return null; |
| } |
| |
| /** |
| * Resolves a bundle that contains tld files as a URL. The URLs are |
| * used by jasper to discover the tld files. |
| * |
| * Support only 2 types of packaging for the bundle: - the bundle is a jar |
| * (recommended for runtime.) - the bundle is a folder and contain jars in |
| * the root and/or in the lib folder (nice for PDE developement situations) |
| * Unsupported: the bundle is a jar that embeds more jars. |
| * |
| * @param locatorHelper |
| * @param bundle |
| * @param urls |
| * @throws Exception |
| */ |
| private void convertBundleLocationToURL(BundleFileLocatorHelper locatorHelper, Bundle bundle, Set<URL> urls) throws Exception |
| { |
| File jasperLocation = locatorHelper.getBundleInstallLocation(bundle); |
| if (jasperLocation.isDirectory()) |
| { |
| for (File f : jasperLocation.listFiles()) |
| { |
| if (f.getName().endsWith(".jar") && f.isFile()) |
| { |
| urls.add(f.toURI().toURL()); |
| } |
| else if (f.isDirectory() && f.getName().equals("lib")) |
| { |
| for (File f2 : jasperLocation.listFiles()) |
| { |
| if (f2.getName().endsWith(".jar") && f2.isFile()) |
| { |
| urls.add(f2.toURI().toURL()); |
| } |
| } |
| } |
| } |
| urls.add(jasperLocation.toURI().toURL()); |
| } |
| else |
| { |
| urls.add(jasperLocation.toURI().toURL()); |
| } |
| } |
| } |