1 // ======================================================================== 2 // Copyright (c) 2009-2010 Intalio, Inc. 3 // ------------------------------------------------------------------------ 4 // All rights reserved. This program and the accompanying materials 5 // are made available under the terms of the Eclipse Public License v1.0 6 // and Apache License v2.0 which accompanies this distribution. 7 // The Eclipse Public License is available at 8 // http://www.eclipse.org/legal/epl-v10.html 9 // The Apache License v2.0 is available at 10 // http://www.opensource.org/licenses/apache2.0.php 11 // You may elect to redistribute this code under either of these licenses. 12 // ======================================================================== 13 package org.eclipse.jetty.osgi.boot.jasper; 14 15 import java.io.File; 16 import java.net.URL; 17 import java.util.ArrayList; 18 import java.util.Collection; 19 import java.util.Collections; 20 import java.util.HashSet; 21 import java.util.List; 22 import java.util.StringTokenizer; 23 24 import org.eclipse.jetty.osgi.boot.OSGiAppProvider; 25 import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper; 26 import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer; 27 import org.osgi.framework.Bundle; 28 import org.osgi.framework.FrameworkUtil; 29 30 /** 31 * Plug bundles that contains tld files so that jasper will discover them and 32 * set them up in jetty. 33 * 34 * For example: 35 * -Dorg.eclipse.jetty.osgi.tldbundles=org.springframework.web.servlet 36 * ,com.opensymphony.module.sitemesh Otherwise use an attribute to the 37 * WebAppDeployer <New 38 * class="org.eclipse.jetty.deploy.providers.WebAppProvider"> .... <Set 39 * name="tldBundles"><Property name="org.eclipse.jetty.osgi.tldsbundles" 40 * default="" /></Set> <New> 41 */ 42 public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistrationCustomizer 43 { 44 /** 45 * To plug into jasper bundles that contain tld files please use a list of 46 * bundle's symbolic names: 47 * -Djetty.osgi.tldbundles=org.springframework.web.servlet 48 * ,com.opensymphony.module.sitemesh 49 */ 50 public static final String SYS_PROP_TLD_BUNDLES = "org.eclipse.jetty.osgi.tldbundles"; 51 52 /** 53 * Union of the tld bundles defined system wide and the one defines as an 54 * attribute of the AppProvider. 55 * 56 * @param provider 57 * @return 58 */ 59 private static Collection<String> getTldBundles(OSGiAppProvider provider) 60 { 61 String sysprop = System.getProperty(SYS_PROP_TLD_BUNDLES); 62 String att = (String) provider.getTldBundles(); 63 if (sysprop == null && att == null) { return Collections.emptySet(); } 64 if (att == null) 65 { 66 att = sysprop; 67 } 68 else if (sysprop != null) 69 { 70 att = att + "," + sysprop; 71 } 72 73 Collection<String> tldbundles = new HashSet<String>(); 74 StringTokenizer tokenizer = new StringTokenizer(att, ", \n\r\t", false); 75 while (tokenizer.hasMoreTokens()) 76 { 77 tldbundles.add(tokenizer.nextToken()); 78 } 79 return tldbundles; 80 } 81 82 /** 83 * @return The location of the jars that contain tld files. Jasper will 84 * discover them. 85 */ 86 public URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper locatorHelper) throws Exception 87 { 88 List<URL> urls = new ArrayList<URL>(); 89 // naive way of finding those bundles. 90 // lots of assumptions: for example we assume a single version of each 91 // bundle that would contain tld files. 92 // this is probably good enough as those tlds are loaded system-wide on 93 // jetty. 94 // to do better than this we need to do it on a per webapp basis. 95 // probably using custom properties in the ContextHandler service 96 // and mirroring those in the MANIFEST.MF 97 98 Bundle[] bundles = FrameworkUtil.getBundle(PluggableWebAppRegistrationCustomizerImpl.class).getBundleContext().getBundles(); 99 Collection<String> tldbundles = getTldBundles(provider); 100 for (Bundle bundle : bundles) 101 { 102 if (tldbundles.contains(bundle.getSymbolicName())) 103 { 104 registerTldBundle(locatorHelper, bundle, urls); 105 } 106 } 107 108 return urls.toArray(new URL[urls.size()]); 109 110 } 111 112 /** 113 * Resolves the bundle that contains tld files as a set of URLs that will be 114 * passed to jasper as a URLClassLoader later on. Usually that would be a 115 * single URL per bundle. But we do some more work if there are jars 116 * embedded in the bundle. 117 * 118 * The jasper TldScanner expects a URLClassloader to parse a jar for the 119 * /META-INF/*.tld it may contain. We place the bundles that we know contain 120 * such tag-libraries. Please note that it will work if and only if the 121 * bundle is a jar (!) Currently we just hardcode the bundle that contains 122 * the jstl implemenation. 123 * 124 * A workaround when the tld cannot be parsed with this method is to copy 125 * and paste it inside the WEB-INF of the webapplication where it is used. 126 * 127 * Support only 2 types of packaging for the bundle: - the bundle is a jar 128 * (recommended for runtime.) - the bundle is a folder and contain jars in 129 * the root and/or in the lib folder (nice for PDE developement situations) 130 * Unsupported: the bundle is a jar that embeds more jars. 131 * 132 * @param locatorHelper 133 * @param bundle 134 * @param urls 135 * @throws Exception 136 */ 137 private void registerTldBundle(BundleFileLocatorHelper locatorHelper, Bundle bundle, List<URL> urls) throws Exception 138 { 139 File jasperLocation = locatorHelper.getBundleInstallLocation(bundle); 140 if (jasperLocation.isDirectory()) 141 { 142 for (File f : jasperLocation.listFiles()) 143 { 144 if (f.getName().endsWith(".jar") && f.isFile()) 145 { 146 urls.add(f.toURI().toURL()); 147 } 148 else if (f.isDirectory() && f.getName().equals("lib")) 149 { 150 for (File f2 : jasperLocation.listFiles()) 151 { 152 if (f2.getName().endsWith(".jar") && f2.isFile()) 153 { 154 urls.add(f2.toURI().toURL()); 155 } 156 } 157 } 158 } 159 urls.add(jasperLocation.toURI().toURL()); 160 } 161 else 162 { 163 urls.add(jasperLocation.toURI().toURL()); 164 } 165 166 } 167 168 }