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.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  /**
34   * LibExtClassLoaderHelper
35   * <p>
36   * Helper to create a URL class-loader with the jars inside
37   * <code>${jetty.home}/lib/ext</code> and <code>${jetty.home}/resources</code>. 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   * The drawback is that those jars will not be available in the OSGi
44   * classloader.
45   * <p>
46   * Alternatives to placing jars in lib/ext:
47   * <ol>
48   * <li>Bundle the jars in an osgi bundle. Have the webapp(s) that need these jars
49   * depend on that bundle.</li>
50   * <li>Bundle those jars in an osgi bundle-fragment that targets the
51   * jetty-bootstrap bundle</li>
52   * <li>Use equinox Buddy-Policy: register a buddy of the jetty bootstrapper
53   * bundle. (Note: it will work only on equinox)</li>
54   * </ol>
55   */
56  public class LibExtClassLoaderHelper
57  {
58      /* ------------------------------------------------------------ */
59      /**
60       * IFilesInJettyHomeResourcesProcessor
61       * 
62       * Interface for callback impls
63       */
64      public interface IFilesInJettyHomeResourcesProcessor
65      {
66          void processFilesInResourcesFolder(File jettyHome, Map<String, File> filesInResourcesFolder);
67      }
68  
69      
70      
71      public static final Set<IFilesInJettyHomeResourcesProcessor> registeredFilesInJettyHomeResourcesProcessors = new HashSet<IFilesInJettyHomeResourcesProcessor>();
72  
73      
74      /* ------------------------------------------------------------ */
75      /**
76       * @param jettyHome the jetty home 
77       * @param parentClassLoader the parent classloader
78       * @return a url classloader with the jars of resources, lib/ext and the
79       *         jars passed in the other argument. The parent classloader usually
80       *         is the JettyBootStrapper (an osgi classloader.
81       * @throws MalformedURLException if the jetty home reference is invalid
82       */
83      public static ClassLoader createLibEtcClassLoader(File jettyHome, ClassLoader parentClassLoader) throws MalformedURLException
84      {
85          if (jettyHome == null) { return parentClassLoader; }
86          ArrayList<URL> urls = new ArrayList<URL>();
87          File jettyResources = new File(jettyHome, "resources");
88          if (jettyResources.exists())
89          {
90              // make sure it contains something else than README:
91              Map<String, File> jettyResFiles = new HashMap<String, File>();
92              for (File f : jettyResources.listFiles())
93              {
94                  jettyResFiles.put(f.getName(), f);
95                  if (f.getName().toLowerCase(Locale.ENGLISH).startsWith("readme"))
96                  {
97                      continue;
98                  }
99                  else
100                 {
101                     if (urls.isEmpty())
102                     {
103                         urls.add(jettyResources.toURI().toURL());
104                     }
105                 }
106             }
107             processFilesInResourcesFolder(jettyHome, jettyResFiles);
108         }
109         File libExt = new File(jettyHome, "lib/ext");
110         if (libExt.exists())
111         {
112             for (File f : libExt.listFiles())
113             {
114                 if (f.getName().endsWith(".jar"))
115                 {
116                     // cheap to tolerate folders so let's do it.
117                     URL url = f.toURI().toURL();
118                     if (f.isFile())
119                     {// is this necessary anyways?
120                         url = new URL("jar:" + url.toString() + "!/");
121                     }
122                     urls.add(url);
123                 }
124             }
125         }
126 
127         return new URLClassLoader(urls.toArray(new URL[urls.size()]), parentClassLoader);
128     }
129 
130     
131     /* ------------------------------------------------------------ */
132     /**
133      * @param jarsContainerOrJars the jars via file references
134      * @param otherJarsOrFolder more jars via url references
135      * @param parentClassLoader the parent classloader
136      * @return a url classloader with the jars of resources, lib/ext and the
137      *         jars passed in the other argument. The parent classloader usually
138      *         is the JettyBootStrapper (an osgi classloader). If there was no
139      *         extra jars to insert, then just return the parentClassLoader.
140      * @throws MalformedURLException if there is a bad jar file reference
141      */
142     public static ClassLoader createLibExtClassLoader(List<File> jarsContainerOrJars, List<URL> otherJarsOrFolder, ClassLoader parentClassLoader) 
143     throws MalformedURLException
144     {
145         if (jarsContainerOrJars == null && otherJarsOrFolder == null) { return parentClassLoader; }
146         List<URL> urls = new ArrayList<URL>();
147         if (otherJarsOrFolder != null)
148         {
149             urls.addAll(otherJarsOrFolder);
150         }
151         if (jarsContainerOrJars != null)
152         {
153             for (File libExt : jarsContainerOrJars)
154             {
155                 if (libExt.isDirectory())
156                 {
157                     for (File f : libExt.listFiles())
158                     {
159                         if (f.getName().endsWith(".jar"))
160                         {
161                             // cheap to tolerate folders so let's do it.
162                             URL url = f.toURI().toURL();
163                             if (f.isFile())
164                             {
165                                 // is this necessary anyways?
166                                 url = new URL("jar:" + url.toString() + "!/");
167                             }
168                             urls.add(url);
169                         }
170                     }
171                 }
172             }
173         }
174         return new URLClassLoader(urls.toArray(new URL[urls.size()]), parentClassLoader);
175     }
176 
177     /* ------------------------------------------------------------ */
178     /**
179      * When we find files typically used for central logging configuration we do
180      * what it takes in this method to do what the user expects. Without
181      * depending too much directly on a particular logging framework.
182      * <p>
183      * We can afford to do some implementation specific code for a logging
184      * framework only in a fragment.
185      * <p>
186      * Trying to configure log4j and logback in here.
187      * <p>
188      * We recommend that slf4j jars are all placed in the osgi framework. And a
189      * single implementation if possible packaged as an osgi bundle is there.
190      * @param jettyHome the jetty home reference
191      * @param childrenFiles the map of child files
192      */
193     protected static void processFilesInResourcesFolder(File jettyHome, Map<String, File> childrenFiles)
194     {
195         for (IFilesInJettyHomeResourcesProcessor processor : registeredFilesInJettyHomeResourcesProcessors)
196         {
197             processor.processFilesInResourcesFolder(jettyHome, childrenFiles);
198         }
199     }
200 
201 }