1 //
2 // ========================================================================
3 // Copyright (c) 1995-2015 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 }