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 }