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