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 }