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