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 }