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