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