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