1 //
2 // ========================================================================
3 // Copyright (c) 1995-2016 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.HashSet;
25 import java.util.Set;
26
27 import javax.servlet.jsp.JspFactory;
28
29 import org.eclipse.jetty.deploy.DeploymentManager;
30 import org.eclipse.jetty.osgi.boot.JettyBootstrapActivator;
31 import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
32 import org.eclipse.jetty.osgi.boot.utils.TldBundleDiscoverer;
33 import org.eclipse.jetty.util.log.Log;
34 import org.eclipse.jetty.util.log.Logger;
35 import org.osgi.framework.Bundle;
36 import org.osgi.framework.FrameworkUtil;
37
38 /**
39 *
40 * JSTLBundleDiscoverer
41 *
42 * Fix various shortcomings with the way jasper parses the tld files. Plugs the
43 * JSTL tlds assuming that they are packaged with the bundle that contains the
44 * JSTL classes.
45 * <p>
46 * Pluggable tlds at the server level are handled by
47 * {@link ContainerTldBundleDiscoverer}.
48 * </p>
49 */
50 public class JSTLBundleDiscoverer implements TldBundleDiscoverer
51 {
52 private static final Logger LOG = Log.getLogger(JSTLBundleDiscoverer.class);
53
54
55 /**
56 * Default name of a class that belongs to the jstl bundle. From that class
57 * we locate the corresponding bundle and register it as a bundle that
58 * contains tld files.
59 */
60 private static String DEFAULT_JSTL_BUNDLE_CLASS = "org.apache.taglibs.standard.tag.el.core.WhenTag";
61
62
63
64 /**
65 * Default jsp factory implementation. Idally jasper is osgified and we can
66 * use services. In the mean time we statically set the jsp factory
67 * implementation. bug #299733
68 */
69 private static String DEFAULT_JSP_FACTORY_IMPL_CLASS = "org.apache.jasper.runtime.JspFactoryImpl";
70
71 private static final Set<URL> __tldBundleCache = new HashSet<URL>();
72
73 public JSTLBundleDiscoverer()
74 {
75 try
76 {
77 // sanity check:
78 Class cl = getClass().getClassLoader().loadClass("org.apache.jasper.servlet.JspServlet");
79 }
80 catch (Exception e)
81 {
82 LOG.warn("Unable to locate the JspServlet: jsp support unavailable.", e);
83 return;
84 }
85 try
86 {
87 Class<javax.servlet.ServletContext> servletContextClass = javax.servlet.ServletContext.class;
88 // bug #299733
89 JspFactory fact = JspFactory.getDefaultFactory();
90 if (fact == null)
91 { // bug #299733
92 // JspFactory does a simple
93 // Class.getForName("org.apache.jasper.runtime.JspFactoryImpl")
94 // however its bundles does not import the jasper package
95 // so it fails. let's help things out:
96 fact = (JspFactory) JettyBootstrapActivator.class.getClassLoader().loadClass(DEFAULT_JSP_FACTORY_IMPL_CLASS).newInstance();
97 JspFactory.setDefaultFactory(fact);
98 }
99
100 }
101 catch (Exception e)
102 {
103 LOG.warn("Unable to set the JspFactory: jsp support incomplete.", e);
104 }
105 }
106
107 /**
108 * The jasper TldScanner expects a URLClassloader to parse a jar for the
109 * /META-INF/*.tld it may contain. We place the bundles that we know contain
110 * such tag-libraries. Please note that it will work if and only if the
111 * bundle is a jar (!) Currently we just hardcode the bundle that contains
112 * the jstl implemenation.
113 *
114 * A workaround when the tld cannot be parsed with this method is to copy
115 * and paste it inside the WEB-INF of the webapplication where it is used.
116 *
117 * Support only 2 types of packaging for the bundle: - the bundle is a jar
118 * (recommended for runtime.) - the bundle is a folder and contain jars in
119 * the root and/or in the lib folder (nice for PDE developement situations)
120 * Unsupported: the bundle is a jar that embeds more jars.
121 *
122 * @return array of URLs
123 * @throws Exception
124 */
125 public URL[] getUrlsForBundlesWithTlds(DeploymentManager deployer, BundleFileLocatorHelper locatorHelper) throws Exception
126 {
127
128 ArrayList<URL> urls = new ArrayList<URL>();
129 Class<?> jstlClass = null;
130
131 // Look for the jstl bundle
132 // We assume the jstl's tlds are defined there.
133 // We assume that the jstl bundle is imported by this bundle
134 // So we can look for this class using this bundle's classloader:
135 try
136 {
137 jstlClass = JSTLBundleDiscoverer.class.getClassLoader().loadClass(DEFAULT_JSTL_BUNDLE_CLASS);
138 }
139 catch (ClassNotFoundException e)
140 {
141 LOG.info("jstl not on classpath", e);
142 }
143
144 if (jstlClass != null)
145 {
146 //get the bundle containing jstl
147 Bundle tldBundle = FrameworkUtil.getBundle(jstlClass);
148 File tldBundleLocation = locatorHelper.getBundleInstallLocation(tldBundle);
149
150 if (tldBundleLocation != null && tldBundleLocation.isDirectory())
151 {
152 // try to find the jar files inside this folder
153 for (File f : tldBundleLocation.listFiles())
154 {
155 if (f.getName().endsWith(".jar") && f.isFile())
156 {
157 urls.add(f.toURI().toURL());
158 }
159 else if (f.isDirectory() && f.getName().equals("lib"))
160 {
161 for (File f2 : tldBundleLocation.listFiles())
162 {
163 if (f2.getName().endsWith(".jar") && f2.isFile())
164 {
165 urls.add(f2.toURI().toURL());
166 }
167 }
168 }
169 }
170
171 }
172 else if (tldBundleLocation != null)
173 {
174 urls.add(tldBundleLocation.toURI().toURL());
175
176 String pattern = (String)deployer.getContextAttribute("org.eclipse.jetty.server.webapp.containerIncludeBundlePattern");
177 pattern = (pattern==null?"":pattern);
178 if (!pattern.contains(tldBundle.getSymbolicName()))
179 {
180 pattern += "|"+tldBundle.getSymbolicName();
181 deployer.setContextAttribute("org.eclipse.jetty.server.webapp.containerIncludeBundlePattern", pattern);
182 }
183 }
184 }
185
186 return urls.toArray(new URL[urls.size()]);
187 }
188
189 }