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 }