View Javadoc

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