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 32 * and set them up in jetty. 33 * 34 * For example: -Dorg.eclipse.jetty.osgi.tldbundles=org.springframework.web.servlet,com.opensymphony.module.sitemesh 35 * Otherwise use an attribute to the WebAppDeployer 36 * <New class="org.eclipse.jetty.deploy.providers.WebAppProvider"> 37 * .... 38 * <Set name="tldBundles"><Property name="org.eclipse.jetty.osgi.tldsbundles" default="" /></Set> 39 * <New> 40 */ 41 public class PluggableWebAppRegistrationCustomizerImpl implements WebappRegistrationCustomizer 42 { 43 /** 44 * To plug into jasper bundles that contain tld files 45 * please use a list of bundle's symbolic names: 46 * -Djetty.osgi.tldbundles=org.springframework.web.servlet,com.opensymphony.module.sitemesh 47 */ 48 public static final String SYS_PROP_TLD_BUNDLES = "org.eclipse.jetty.osgi.tldbundles"; 49 50 /** 51 * Union of the tld bundles defined system wide and the one defines as an attribute of the AppProvider. 52 * @param provider 53 * @return 54 */ 55 private static Collection<String> getTldBundles(OSGiAppProvider provider) 56 { 57 String sysprop = System.getProperty(SYS_PROP_TLD_BUNDLES); 58 String att = (String)provider.getTldBundles(); 59 if (sysprop == null && att == null) 60 { 61 return Collections.emptySet(); 62 } 63 if (att == null) 64 { 65 att = sysprop; 66 } 67 else if (sysprop != null) 68 { 69 att = att + "," + sysprop; 70 } 71 72 Collection<String> tldbundles = new HashSet<String>(); 73 StringTokenizer tokenizer = new StringTokenizer(att, ", \n\r\t", false); 74 while (tokenizer.hasMoreTokens()) 75 { 76 tldbundles.add(tokenizer.nextToken()); 77 } 78 return tldbundles; 79 } 80 81 /** 82 * @return The location of the jars that contain tld files. 83 * Jasper will discover them. 84 */ 85 public URL[] getJarsWithTlds(OSGiAppProvider provider, BundleFileLocatorHelper locatorHelper) throws Exception 86 { 87 List<URL> urls = new ArrayList<URL>(); 88 //naive way of finding those bundles. 89 //lots of assumptions: for example we assume a single version of each bundle that would contain tld files. 90 //this is probably good enough as those tlds are loaded system-wide on jetty. 91 //to do better than this we need to do it on a per webapp basis. 92 //probably using custom properties in the ContextHandler service 93 //and mirroring those in the MANIFEST.MF 94 95 Bundle[] bundles = FrameworkUtil.getBundle(PluggableWebAppRegistrationCustomizerImpl.class).getBundleContext().getBundles(); 96 Collection<String> tldbundles = getTldBundles(provider); 97 for (Bundle bundle : bundles) 98 { 99 if (tldbundles.contains(bundle.getSymbolicName())) 100 { 101 registerTldBundle(locatorHelper, bundle, urls); 102 } 103 } 104 105 return urls.toArray(new URL[urls.size()]); 106 107 } 108 109 /** 110 * Resolves the bundle that contains tld files as a set of URLs that will be 111 * passed to jasper as a URLClassLoader later on. 112 * Usually that would be a single URL per bundle. 113 * But we do some more work if there are jars embedded in the bundle. 114 * 115 * The jasper TldScanner expects a URLClassloader to parse a jar for the /META-INF/*.tld it may contain. We place the bundles that we know contain such 116 * tag-libraries. Please note that it will work if and only if the bundle is a jar (!) Currently we just hardcode the bundle that contains the jstl 117 * implemenation. 118 * 119 * A workaround when the tld cannot be parsed with this method is to copy and paste it inside the WEB-INF of the webapplication where it is used. 120 * 121 * 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 122 * and/or in the lib folder (nice for PDE developement situations) Unsupported: the bundle is a jar that embeds more jars. 123 * 124 * @param locatorHelper 125 * @param bundle 126 * @param urls 127 * @throws Exception 128 */ 129 private void registerTldBundle(BundleFileLocatorHelper locatorHelper, Bundle bundle, List<URL> urls) throws Exception 130 { 131 File jasperLocation = locatorHelper.getBundleInstallLocation(bundle); 132 if (jasperLocation.isDirectory()) 133 { 134 for (File f : jasperLocation.listFiles()) 135 { 136 if (f.getName().endsWith(".jar") && f.isFile()) 137 { 138 urls.add(f.toURI().toURL()); 139 } 140 else if (f.isDirectory() && f.getName().equals("lib")) 141 { 142 for (File f2 : jasperLocation.listFiles()) 143 { 144 if (f2.getName().endsWith(".jar") && f2.isFile()) 145 { 146 urls.add(f2.toURI().toURL()); 147 } 148 } 149 } 150 } 151 urls.add(jasperLocation.toURI().toURL()); 152 } 153 else 154 { 155 urls.add(jasperLocation.toURI().toURL()); 156 } 157 158 } 159 160 }