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