View Javadoc

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">&ltProperty 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 }