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