View Javadoc

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 }