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.internal.webapp;
20  
21  import java.io.File;
22  import java.net.MalformedURLException;
23  import java.net.URL;
24  import java.net.URLClassLoader;
25  import java.util.ArrayList;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.List;
29  import java.util.Locale;
30  import java.util.Map;
31  import java.util.Set;
32  
33  import org.eclipse.jetty.server.Server;
34  
35  /**
36   * Helper to create a URL class-loader with the jars inside
37   * ${jetty.home}/lib/ext and ${jetty.home}/resources. In an ideal world, every
38   * library is an OSGi bundle that does loads nicely. To support standard jars or
39   * bundles that cannot be loaded in the current OSGi environment, we support
40   * inserting the jars in the usual jetty/lib/ext folders in the proper classpath
41   * for the webapps.
42   * <p>
43   * Also the folder resources typically contains central configuration files for
44   * things like: log config and others. We enable fragments to register classes
45   * that are called back and passed those resources to do what they need to do.
46   * </p>
47   * <p>
48   * For example the test-jndi webapplication depends on derby, derbytools,
49   * atomikos none of them are osgi bundles. we can either re-package them or we
50   * can place them in the usual lib/ext. <br/>
51   * In fact jasper's jsp libraries should maybe place in lib/ext too.
52   * </p>
53   * <p>
54   * The drawback is that those libraries will not be available in the OSGi
55   * classloader. Note that we could have setup those jars as embedded jars of the
56   * current bundle. However, we would need to know in advance what are those jars
57   * which was not acceptable. Also having those jars in a URLClassLoader seem to
58   * be required for some cases. For example jaspers' TldLocationsCache (replaced
59   * by TldScanner for servlet-3.0). <br/>
60   * Also all the dependencies of those libraries must be resolvable directly from
61   * the JettyBootstrapActivator bundle as it is set as the parent classloader. For
62   * example: if atomikos is placed in lib/ext it will work if and only if
63   * JettyBootstrapActivator import the necessary packages from javax.naming*,
64   * javax.transaction*, javax.mail* etc Most of the common cases of javax are
65   * added as optional import packages into jetty bootstrapper plugin. When there
66   * are not covered: please make a request or create a fragment or register a
67   * bundle with a buddy-policy onto the jetty bootstrapper..
68   * </p>
69   * <p>
70   * Alternatives to placing jars in lib/ext
71   * <ol>
72   * <li>Bundle the jars in an osgi bundle. Have the webapp(s) that context
73   * depends on them depend on that bundle. Things will go well for jetty.</li>
74   * <li>Bundle those jars in an osgi bundle-fragment that targets the
75   * jetty-bootstrap bundle</li>
76   * <li>Use equinox Buddy-Policy: register a buddy of the jetty bootstrapper
77   * bundle. (least favorite: it will work only on equinox)</li>
78   * </ol>
79   * </p>
80   */
81  public class LibExtClassLoaderHelper
82  {
83  
84      /**
85       * Class called back
86       */
87      public interface IFilesInJettyHomeResourcesProcessor
88      {
89          void processFilesInResourcesFolder(File jettyHome, Map<String, File> filesInResourcesFolder);
90      }
91  
92      public static Set<IFilesInJettyHomeResourcesProcessor> registeredFilesInJettyHomeResourcesProcessors = new HashSet<IFilesInJettyHomeResourcesProcessor>();
93  
94      /**
95       * @param server
96       * @return a url classloader with the jars of resources, lib/ext and the
97       *         jars passed in the other argument. The parent classloader usually
98       *         is the JettyBootStrapper (an osgi classloader.
99       * @throws MalformedURLException
100      */
101     public static ClassLoader createLibEtcClassLoader(File jettyHome, Server server, ClassLoader parentClassLoader) throws MalformedURLException
102     {
103         if (jettyHome == null) { return parentClassLoader; }
104         ArrayList<URL> urls = new ArrayList<URL>();
105         File jettyResources = new File(jettyHome, "resources");
106         if (jettyResources.exists())
107         {
108             // make sure it contains something else than README:
109             Map<String, File> jettyResFiles = new HashMap<String, File>();
110             for (File f : jettyResources.listFiles())
111             {
112                 jettyResFiles.put(f.getName(), f);
113                 if (f.getName().toLowerCase(Locale.ENGLISH).startsWith("readme"))
114                 {
115                     continue;
116                 }
117                 else
118                 {
119                     if (urls.isEmpty())
120                     {
121                         urls.add(jettyResources.toURI().toURL());
122                     }
123                 }
124             }
125             processFilesInResourcesFolder(jettyHome, jettyResFiles);
126         }
127         File libExt = new File(jettyHome, "lib/ext");
128         if (libExt.exists())
129         {
130             for (File f : libExt.listFiles())
131             {
132                 if (f.getName().endsWith(".jar"))
133                 {
134                     // cheap to tolerate folders so let's do it.
135                     URL url = f.toURI().toURL();
136                     if (f.isFile())
137                     {// is this necessary anyways?
138                         url = new URL("jar:" + url.toString() + "!/");
139                     }
140                     urls.add(url);
141                 }
142             }
143         }
144 
145         return new URLClassLoader(urls.toArray(new URL[urls.size()]), parentClassLoader);
146     }
147 
148     /**
149      * @param server
150      * @return a url classloader with the jars of resources, lib/ext and the
151      *         jars passed in the other argument. The parent classloader usually
152      *         is the JettyBootStrapper (an osgi classloader). If there was no
153      *         extra jars to insert, then just return the parentClassLoader.
154      * @throws MalformedURLException
155      */
156     public static ClassLoader createLibExtClassLoader(List<File> jarsContainerOrJars, List<URL> otherJarsOrFolder, Server server, ClassLoader parentClassLoader) 
157     throws MalformedURLException
158     {
159         if (jarsContainerOrJars == null && otherJarsOrFolder == null) { return parentClassLoader; }
160         List<URL> urls = new ArrayList<URL>();
161         if (otherJarsOrFolder != null)
162         {
163             urls.addAll(otherJarsOrFolder);
164         }
165         if (jarsContainerOrJars != null)
166         {
167             for (File libExt : jarsContainerOrJars)
168             {
169                 if (libExt.isDirectory())
170                 {
171                     for (File f : libExt.listFiles())
172                     {
173                         if (f.getName().endsWith(".jar"))
174                         {
175                             // cheap to tolerate folders so let's do it.
176                             URL url = f.toURI().toURL();
177                             if (f.isFile())
178                             {
179                                 // is this necessary anyways?
180                                 url = new URL("jar:" + url.toString() + "!/");
181                             }
182                             urls.add(url);
183                         }
184                     }
185                 }
186             }
187         }
188         return new URLClassLoader(urls.toArray(new URL[urls.size()]), parentClassLoader);
189     }
190 
191     /**
192      * When we find files typically used for central logging configuration we do
193      * what it takes in this method to do what the user expects. Without
194      * depending too much directly on a particular logging framework.
195      * <p>
196      * We can afford to do some implementation specific code for a logging
197      * framework only in a fragment. <br/>
198      * Trying to configure log4j and logback in here.
199      * </p>
200      * <p>
201      * We recommend that slf4j jars are all placed in the osgi framework. And a
202      * single implementation if possible packaged as an osgi bundle is there.
203      * </p>
204      */
205     protected static void processFilesInResourcesFolder(File jettyHome, Map<String, File> childrenFiles)
206     {
207         for (IFilesInJettyHomeResourcesProcessor processor : registeredFilesInJettyHomeResourcesProcessors)
208         {
209             processor.processFilesInResourcesFolder(jettyHome, childrenFiles);
210         }
211     }
212 
213 }