View Javadoc

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">&ltProperty 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 }