blob: cc96f81ef8595e3e886ec37a16888d86d2419c7f [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.osgi.boot.internal.webapp;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
/**
* LibExtClassLoaderHelper
* <p>
* Helper to create a URL class-loader with the jars inside
* <code>${jetty.home}/lib/ext</code> and <code>${jetty.home}/resources</code>. In an ideal world, every
* library is an OSGi bundle that does loads nicely. To support standard jars or
* bundles that cannot be loaded in the current OSGi environment, we support
* inserting the jars in the usual jetty/lib/ext folders in the proper classpath
* for the webapps.
* <p>
* The drawback is that those jars will not be available in the OSGi
* classloader.
* <p>
* Alternatives to placing jars in lib/ext:
* <ol>
* <li>Bundle the jars in an osgi bundle. Have the webapp(s) that need these jars
* depend on that bundle.</li>
* <li>Bundle those jars in an osgi bundle-fragment that targets the
* jetty-bootstrap bundle</li>
* <li>Use equinox Buddy-Policy: register a buddy of the jetty bootstrapper
* bundle. (Note: it will work only on equinox)</li>
* </ol>
*/
public class LibExtClassLoaderHelper
{
/* ------------------------------------------------------------ */
/**
* IFilesInJettyHomeResourcesProcessor
*
* Interface for callback impls
*/
public interface IFilesInJettyHomeResourcesProcessor
{
void processFilesInResourcesFolder(File jettyHome, Map<String, File> filesInResourcesFolder);
}
public static final Set<IFilesInJettyHomeResourcesProcessor> registeredFilesInJettyHomeResourcesProcessors = new HashSet<IFilesInJettyHomeResourcesProcessor>();
/* ------------------------------------------------------------ */
/**
* @param jettyHome the jetty home
* @param parentClassLoader the parent classloader
* @return a url classloader with the jars of resources, lib/ext and the
* jars passed in the other argument. The parent classloader usually
* is the JettyBootStrapper (an osgi classloader.
* @throws MalformedURLException if the jetty home reference is invalid
*/
public static ClassLoader createLibEtcClassLoader(File jettyHome, ClassLoader parentClassLoader) throws MalformedURLException
{
if (jettyHome == null) { return parentClassLoader; }
ArrayList<URL> urls = new ArrayList<URL>();
File jettyResources = new File(jettyHome, "resources");
if (jettyResources.exists())
{
// make sure it contains something else than README:
Map<String, File> jettyResFiles = new HashMap<String, File>();
for (File f : jettyResources.listFiles())
{
jettyResFiles.put(f.getName(), f);
if (f.getName().toLowerCase(Locale.ENGLISH).startsWith("readme"))
{
continue;
}
else
{
if (urls.isEmpty())
{
urls.add(jettyResources.toURI().toURL());
}
}
}
processFilesInResourcesFolder(jettyHome, jettyResFiles);
}
File libExt = new File(jettyHome, "lib/ext");
if (libExt.exists())
{
for (File f : libExt.listFiles())
{
if (f.getName().endsWith(".jar"))
{
// cheap to tolerate folders so let's do it.
URL url = f.toURI().toURL();
if (f.isFile())
{// is this necessary anyways?
url = new URL("jar:" + url.toString() + "!/");
}
urls.add(url);
}
}
}
return new URLClassLoader(urls.toArray(new URL[urls.size()]), parentClassLoader);
}
/* ------------------------------------------------------------ */
/**
* @param jarsContainerOrJars the jars via file references
* @param otherJarsOrFolder more jars via url references
* @param parentClassLoader the parent classloader
* @return a url classloader with the jars of resources, lib/ext and the
* jars passed in the other argument. The parent classloader usually
* is the JettyBootStrapper (an osgi classloader). If there was no
* extra jars to insert, then just return the parentClassLoader.
* @throws MalformedURLException if there is a bad jar file reference
*/
public static ClassLoader createLibExtClassLoader(List<File> jarsContainerOrJars, List<URL> otherJarsOrFolder, ClassLoader parentClassLoader)
throws MalformedURLException
{
if (jarsContainerOrJars == null && otherJarsOrFolder == null) { return parentClassLoader; }
List<URL> urls = new ArrayList<URL>();
if (otherJarsOrFolder != null)
{
urls.addAll(otherJarsOrFolder);
}
if (jarsContainerOrJars != null)
{
for (File libExt : jarsContainerOrJars)
{
if (libExt.isDirectory())
{
for (File f : libExt.listFiles())
{
if (f.getName().endsWith(".jar"))
{
// cheap to tolerate folders so let's do it.
URL url = f.toURI().toURL();
if (f.isFile())
{
// is this necessary anyways?
url = new URL("jar:" + url.toString() + "!/");
}
urls.add(url);
}
}
}
}
}
return new URLClassLoader(urls.toArray(new URL[urls.size()]), parentClassLoader);
}
/* ------------------------------------------------------------ */
/**
* When we find files typically used for central logging configuration we do
* what it takes in this method to do what the user expects. Without
* depending too much directly on a particular logging framework.
* <p>
* We can afford to do some implementation specific code for a logging
* framework only in a fragment.
* <p>
* Trying to configure log4j and logback in here.
* <p>
* We recommend that slf4j jars are all placed in the osgi framework. And a
* single implementation if possible packaged as an osgi bundle is there.
* @param jettyHome the jetty home reference
* @param childrenFiles the map of child files
*/
protected static void processFilesInResourcesFolder(File jettyHome, Map<String, File> childrenFiles)
{
for (IFilesInJettyHomeResourcesProcessor processor : registeredFilesInJettyHomeResourcesProcessors)
{
processor.processFilesInResourcesFolder(jettyHome, childrenFiles);
}
}
}