Jetty 12.0.x osgi (#9068)

This commit is contained in:
Jan Bartel 2022-12-22 01:12:44 +11:00 committed by GitHub
parent 964e44527d
commit 1abaeb6cee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
201 changed files with 3577 additions and 12237 deletions

View File

@ -192,6 +192,7 @@ public class MimeTypes
.withAll(() ->
{
Map<String, Type> result = new HashMap<>();
for (Type type : Type.values())
{
String key1 = type.toString();

View File

@ -0,0 +1,158 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-core</artifactId>
<version>12.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-osgi</artifactId>
<name>Jetty Core :: OSGi</name>
<description>Support for OSGi integration</description>
<properties>
<bundle-symbolic-name>${project.groupId}.osgi</bundle-symbolic-name>
<spotbugs.onlyAnalyze>org.eclipse.jetty.osgi.*</spotbugs.onlyAnalyze>
<osgi-version>3.18.100</osgi-version>
<osgi-services-version>3.11.0</osgi-services-version>
<osgi-service-cm-version>1.6.1</osgi-service-cm-version>
<osgi-service-component-version>1.5.0</osgi-service-component-version>
<osgi-service-event-version>1.4.1</osgi-service-event-version>
<osgi-util-version>3.7.100</osgi-util-version>
<osgi-util-function-version>1.2.0</osgi-util-function-version>
<osgi-util-promise-version>1.2.0</osgi-util-promise-version>
<osgi-util-measurement-version>1.0.2</osgi-util-measurement-version>
<osgi-util-position-version>1.0.1</osgi-util-position-version>
<osgi-util-tracker-version>1.5.4</osgi-util-tracker-version>
<osgi-util-xml-version>1.0.2</osgi-util-xml-version>
<equinox-http-servlet-version>1.0.0-v20070606</equinox-http-servlet-version>
<jacoco.skip>true</jacoco.skip>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
@{argLine} ${jetty.surefire.argLine} --add-reads org.eclipse.jetty.server=org.eclipse.jetty.logging --add-opens org.eclipse.jetty.server/org.eclipse.jetty.server=ALL-UNNAMED
</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-Activator>org.eclipse.jetty.osgi.JettyBootstrapActivator</Bundle-Activator>
<DynamicImport-Package>org.eclipse.jetty.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))"</DynamicImport-Package>
<_nouses>true</_nouses>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
<version>${osgi-services-version}</version>
<exclusions>
<exclusion>
<!-- we use the servlet jar from orbit -->
<groupId>jakarta.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<!-- cannot override core java classes with Java 9+ -->
<groupId>org.apache.felix</groupId>
<artifactId>org.osgi.foundation</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.osgi</artifactId>
<version>${osgi-version}</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.cm</artifactId>
<version>${osgi-service-cm-version}</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.component</artifactId>
<version>${osgi-service-component-version}</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.event</artifactId>
<version>${osgi-service-event-version}</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.util.tracker</artifactId>
<version>${osgi-util-tracker-version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.equinox.http</groupId>
<artifactId>servlet</artifactId>
<version>${equinox-http-servlet-version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-ee</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-deploy</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.osgi</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.cm</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.event</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.util.tracker</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,286 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.osgi;
import java.io.File;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.ee.Deployable;
import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.util.OSGiClassLoader;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.Environment;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* AbstractContextProvider
*
* Base class for DeploymentManager Providers that can deploy ContextHandlers into
* Jetty that have been discovered via OSGI either as bundles or services.
*/
public abstract class AbstractContextProvider extends AbstractLifeCycle implements AppProvider
{
private static final Logger LOG = LoggerFactory.getLogger(AbstractContextProvider.class);
private DeploymentManager _deploymentManager;
private Server _server;
private ContextFactory _contextFactory;
private String _environment;
private final Map<String, String> _properties = new HashMap<>();
public AbstractContextProvider(String environment, Server server, ContextFactory contextFactory)
{
_environment = Objects.requireNonNull(environment);
_server = Objects.requireNonNull(server);
_contextFactory = Objects.requireNonNull(contextFactory);
}
public Server getServer()
{
return _server;
}
public Map<String, String> getProperties()
{
return _properties;
}
@Override
public ContextHandler createContextHandler(App app) throws Exception
{
if (app == null)
return null;
//Create a ContextHandler suitable to deploy in OSGi
ContextHandler h = _contextFactory.createContextHandler(this, app);
return h;
}
@Override
public void setDeploymentManager(DeploymentManager deploymentManager)
{
_deploymentManager = deploymentManager;
}
@Override
public String getEnvironmentName()
{
return _environment;
}
public DeploymentManager getDeploymentManager()
{
return _deploymentManager;
}
/**
* Get the extractWars.
* This is equivalent to getting the {@link Deployable#EXTRACT_WARS} property.
*
* @return the extractWars
*/
public boolean isExtractWars()
{
return Boolean.parseBoolean(_properties.get(Deployable.EXTRACT_WARS));
}
/**
* Set the extractWars.
* This is equivalent to setting the {@link Deployable#EXTRACT_WARS} property.
*
* @param extractWars the extractWars to set
*/
public void setExtractWars(boolean extractWars)
{
_properties.put(Deployable.EXTRACT_WARS, Boolean.toString(extractWars));
}
/**
* Get the parentLoaderPriority.
* This is equivalent to getting the {@link Deployable#PARENT_LOADER_PRIORITY} property.
*
* @return the parentLoaderPriority
*/
public boolean isParentLoaderPriority()
{
return Boolean.parseBoolean(_properties.get(Deployable.PARENT_LOADER_PRIORITY));
}
/**
* Set the parentLoaderPriority.
* This is equivalent to setting the {@link Deployable#PARENT_LOADER_PRIORITY} property.
*
* @param parentLoaderPriority the parentLoaderPriority to set
*/
public void setParentLoaderPriority(boolean parentLoaderPriority)
{
_properties.put(Deployable.PARENT_LOADER_PRIORITY, Boolean.toString(parentLoaderPriority));
}
/**
* Get the defaultsDescriptor.
* This is equivalent to getting the {@link Deployable#DEFAULTS_DESCRIPTOR} property.
*
* @return the defaultsDescriptor
*/
public String getDefaultsDescriptor()
{
return _properties.get(Deployable.DEFAULTS_DESCRIPTOR);
}
/**
* Set the defaultsDescriptor.
* This is equivalent to setting the {@link Deployable#DEFAULTS_DESCRIPTOR} property.
*
* @param defaultsDescriptor the defaultsDescriptor to set
*/
public void setDefaultsDescriptor(String defaultsDescriptor)
{
_properties.put(Deployable.DEFAULTS_DESCRIPTOR, defaultsDescriptor);
}
/**
* This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} property.
* @param configurations The configuration class names as a comma separated list
*/
public void setConfigurationClasses(String configurations)
{
setConfigurationClasses(StringUtil.isBlank(configurations) ? null : configurations.split(","));
}
/**
* This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} property.
* @param configurations The configuration class names.
*/
public void setConfigurationClasses(String[] configurations)
{
_properties.put(Deployable.CONFIGURATION_CLASSES, (configurations == null)
? null
: String.join(",", configurations));
}
/**
*
* This is equivalent to getting the {@link Deployable#CONFIGURATION_CLASSES} property.
* @return The configuration class names.
*/
public String[] getConfigurationClasses()
{
String cc = _properties.get(Deployable.CONFIGURATION_CLASSES);
return cc == null ? new String[0] : cc.split(",");
}
/**
* Set the temporary directory for deployment.
* <p>
* This is equivalent to setting the {@link Deployable#BASE_TEMP_DIR} property.
* If not set, then the <code>java.io.tmpdir</code> System Property is used.
*
* @param directory the new work directory
*/
public void setTempDir(String directory)
{
_properties.put(Deployable.BASE_TEMP_DIR, directory);
}
/**
* Set the temporary directory for deployment.
* <p>
* This is equivalent to setting the {@link Deployable#BASE_TEMP_DIR} property.
* If not set, then the <code>java.io.tmpdir</code> System Property is used.
*
* @param directory the new work directory
*/
public void setTempDir(File directory)
{
_properties.put(Deployable.BASE_TEMP_DIR, directory.getAbsolutePath());
}
/**
* Get the temporary directory for deployment.
* <p>
* This is equivalent to getting the {@link Deployable#BASE_TEMP_DIR} property.
*
* @return the user supplied work directory (null if user has not set Temp Directory yet)
*/
public File getTempDir()
{
String tmpDir = _properties.get(Deployable.BASE_TEMP_DIR);
return tmpDir == null ? null : new File(tmpDir);
}
/**
* @param tldBundles Comma separated list of bundles that contain tld jars
* that should be setup on the context instances created here.
*/
public void setTldBundles(String tldBundles)
{
_properties.put(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, tldBundles);
}
/**
* @return The list of bundles that contain tld jars that should be setup on
* the contexts create here.
*/
public String getTldBundles()
{
return _properties.get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
}
public boolean isDeployable(Bundle bundle)
{
if (bundle == null)
return false;
//check environment matches
if (getEnvironmentName().equalsIgnoreCase(bundle.getHeaders().get(OSGiWebappConstants.JETTY_ENVIRONMENT)))
return true;
return false;
}
public boolean isDeployable(ServiceReference service)
{
if (service == null)
return false;
//has it been deployed before?
if (!StringUtil.isBlank((String)service.getProperty(OSGiWebappConstants.WATERMARK)))
return false;
//destined for our environment?
if (getEnvironmentName().equalsIgnoreCase((String)service.getProperty(OSGiWebappConstants.JETTY_ENVIRONMENT)))
return true;
return false;
}
}

View File

@ -11,8 +11,11 @@
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot;
package org.eclipse.jetty.osgi;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.HashMap;
@ -21,7 +24,8 @@ import java.util.List;
import java.util.Map;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.osgi.util.Util;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.StringUtil;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
@ -41,22 +45,31 @@ public class BundleContextProvider extends AbstractContextProvider implements Bu
{
private static final Logger LOG = LoggerFactory.getLogger(AbstractContextProvider.class);
private Map<String, App> _appMap = new HashMap<String, App>();
private Map<Path, App> _appMap = new HashMap<>();
private Map<Bundle, List<App>> _bundleMap = new HashMap<Bundle, List<App>>();
private Map<Bundle, List<App>> _bundleMap = new HashMap<>();
private ServiceRegistration _serviceRegForBundles;
private BundleTracker _tracker;
/**
* ContextBundleTracker
*
* Track deployment of Bundles that should be deployed to Jetty as contexts.
*/
public class ContextBundleTracker extends BundleTracker
{
protected String _managedServerName;
protected String _serverName;
public ContextBundleTracker(BundleContext bundleContext, String managedServerName)
/**
* @param bundleContext our bundle
* @param serverName the Server instance to which we will deploy contexts
*/
public ContextBundleTracker(BundleContext bundleContext, String serverName)
{
super(bundleContext, Bundle.ACTIVE | Bundle.STOPPING, null);
_managedServerName = managedServerName;
_serverName = serverName;
}
@Override
@ -65,8 +78,8 @@ public class BundleContextProvider extends AbstractContextProvider implements Bu
try
{
String serverName = (String)bundle.getHeaders().get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
if ((StringUtil.isBlank(serverName) && _managedServerName.equals(OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME)) ||
(!StringUtil.isBlank(serverName) && (serverName.equals(_managedServerName))))
if ((StringUtil.isBlank(serverName) && _serverName.equals(OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME)) ||
(!StringUtil.isBlank(serverName) && (serverName.equals(_serverName))))
{
if (bundleAdded(bundle))
return bundle;
@ -93,21 +106,23 @@ public class BundleContextProvider extends AbstractContextProvider implements Bu
}
}
public BundleContextProvider(ServerInstanceWrapper wrapper)
public BundleContextProvider(String environment, Server server, ContextFactory contextFactory)
{
super(wrapper);
super(environment, server, contextFactory);
}
@Override
protected void doStart() throws Exception
{
String serverName = (String)getServer().getAttribute(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
//Track bundles that are ContextHandlers that should be deployed
_tracker = new ContextBundleTracker(FrameworkUtil.getBundle(this.getClass()).getBundleContext(), getServerInstanceWrapper().getManagedServerName());
_tracker = new ContextBundleTracker(FrameworkUtil.getBundle(this.getClass()).getBundleContext(), serverName);
_tracker.open();
//register as an osgi service for deploying contexts defined in a bundle, advertising the name of the jetty Server instance we are related to
Dictionary<String, String> properties = new Hashtable<String, String>();
properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, serverName);
_serviceRegForBundles = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(BundleProvider.class.getName(), this, properties);
super.doStart();
}
@ -131,35 +146,49 @@ public class BundleContextProvider extends AbstractContextProvider implements Bu
}
}
/**
* Deploy a bundle as a Jetty context.
*/
@Override
public boolean bundleAdded(Bundle bundle) throws Exception
{
if (bundle == null)
return false;
if (!isDeployable(bundle))
return false;
//If the bundle defines a Web-ContextPath then its probably a webapp and the BundleWebAppProvider should deploy it
if ((String)bundle.getHeaders().get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH) != null)
if (bundle.getHeaders().get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH) != null)
{
if (LOG.isDebugEnabled())
LOG.debug("BundleContextProvider ignoring bundle {} with {} set", bundle.getSymbolicName(), OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
return false;
}
String contextFiles = (String)bundle.getHeaders().get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
//comma separated list of context xml files, each of which is a separate ContextHandler to deploy
String contextFiles = bundle.getHeaders().get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
//no contexts to deploy, ignore it
if (contextFiles == null)
return false;
//is the bundle destined for my environment?
String jettyHome = (String)getServer().getAttribute(OSGiServerConstants.JETTY_HOME);
Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome));
boolean added = false;
//bundle defines JETTY_CONTEXT_FILE_PATH header,
//a comma separated list of context xml files that each define a ContextHandler
//TODO: (could be WebAppContexts)
String[] tmp = contextFiles.split("[,;]");
for (String contextFile : tmp)
{
String originId = bundle.getSymbolicName() + "-" + bundle.getVersion().toString() + "-" + contextFile;
OSGiApp app = new OSGiApp(getDeploymentManager(), this, originId, bundle, contextFile);
_appMap.put(originId, app);
OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle);
URI contextFilePath = Util.resolvePathAsLocalizedURI(contextFile, app.getBundle(), jettyHomePath);
//set up the single context file for this deployment
app.getProperties().put(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH, contextFilePath.toString());
_appMap.put(app.getPath(), app);
List<App> apps = _bundleMap.get(bundle);
if (apps == null)
{
@ -189,11 +218,13 @@ public class BundleContextProvider extends AbstractContextProvider implements Bu
{
for (App app : apps)
{
_appMap.remove(app.getOriginId());
if (_appMap.remove(app.getPath()) != null)
{
getDeploymentManager().removeApp(app);
removed = true;
}
}
}
return removed; //true if even 1 context was removed associated with this bundle
}
}

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot;
package org.eclipse.jetty.osgi;
import org.osgi.framework.Bundle;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot;
package org.eclipse.jetty.osgi;
import java.util.Dictionary;
import java.util.HashMap;
@ -19,8 +19,8 @@ import java.util.Hashtable;
import java.util.Map;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.ee9.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.ee9.osgi.boot.utils.Util;
import org.eclipse.jetty.osgi.util.Util;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.StringUtil;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
@ -36,9 +36,9 @@ import org.slf4j.LoggerFactory;
* <p>
* A Jetty Provider that knows how to deploy a WebApp contained inside a Bundle.
*/
public class BundleWebAppProvider extends AbstractWebAppProvider implements BundleProvider
public class BundleWebAppProvider extends AbstractContextProvider implements BundleProvider
{
private static final Logger LOG = LoggerFactory.getLogger(AbstractWebAppProvider.class);
private static final Logger LOG = LoggerFactory.getLogger(AbstractContextProvider.class);
/**
* Map of Bundle to App. Used when a Bundle contains a webapp.
@ -65,6 +65,7 @@ public class BundleWebAppProvider extends AbstractWebAppProvider implements Bund
try
{
String serverName = (String)bundle.getHeaders().get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
if ((StringUtil.isBlank(serverName) && _managedServerName.equals(OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME)) ||
(!StringUtil.isBlank(serverName) && (serverName.equals(_managedServerName))))
{
@ -93,19 +94,20 @@ public class BundleWebAppProvider extends AbstractWebAppProvider implements Bund
}
}
public BundleWebAppProvider(ServerInstanceWrapper wrapper)
public BundleWebAppProvider(String environment, Server server, ContextFactory contextFactory)
{
super(wrapper);
super(environment, server, contextFactory);
}
@Override
protected void doStart() throws Exception
{
_webappTracker = new WebAppTracker(FrameworkUtil.getBundle(this.getClass()).getBundleContext(), getServerInstanceWrapper().getManagedServerName());
String serverName = (String)getServer().getAttribute(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
_webappTracker = new WebAppTracker(FrameworkUtil.getBundle(this.getClass()).getBundleContext(), serverName);
_webappTracker.open();
//register as an osgi service for deploying bundles, advertising the name of the jetty Server instance we are related to
Dictionary<String, String> properties = new Hashtable<>();
properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, serverName);
_serviceRegForBundles = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(BundleProvider.class.getName(), this, properties);
super.doStart();
}
@ -131,6 +133,29 @@ public class BundleWebAppProvider extends AbstractWebAppProvider implements Bund
super.doStop();
}
@Override
public boolean isDeployable(Bundle bundle)
{
//is it destined for my environment?
if (!super.isDeployable(bundle))
return false;
//has a war path, could be a webapp
if (!StringUtil.isBlank(Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_RESOURCE_PATH, bundle.getHeaders())))
return true;
//has a context path header, could be a webapp
if (!StringUtil.isBlank(Util.getManifestHeaderValue(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH, bundle.getHeaders())))
return true;
//has a web.xml, could be a webapp
if (bundle.getEntry("/WEB-INF/web.xml") != null)
return true;
//not a webapp
return false;
}
/**
* A bundle has been added that could be a webapp
*
@ -142,28 +167,26 @@ public class BundleWebAppProvider extends AbstractWebAppProvider implements Bund
if (bundle == null)
return false;
//can this bundle be deployed to my environment?
if (!isDeployable(bundle))
return false;
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps());
String contextPath = null;
Thread.currentThread().setContextClassLoader((ClassLoader)getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER));
try
{
@SuppressWarnings("unchecked")
Dictionary<String, String> headers = bundle.getHeaders();
//does the bundle have a OSGiWebappConstants.JETTY_WAR_FOLDER_PATH
String resourcePath = Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_RESOURCE_PATH, headers);
if (resourcePath != null)
String staticResourcesLocation = Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_RESOURCE_PATH, headers);
if (staticResourcesLocation != null)
{
String base = resourcePath;
contextPath = getContextPath(bundle);
String originId = getOriginId(bundle, base);
//TODO : we don't know whether an app is actually deployed, as deploymentManager swallows all
//exceptions inside the impl of addApp. Need to send the Event and also register as a service
//only if the deployment succeeded
OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
app.setWebAppPath(base);
app.setContextPath(contextPath);
OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle);
app.setPathToResourceBase(staticResourcesLocation);
_bundleMap.put(bundle, app);
getDeploymentManager().addApp(app);
return true;
@ -173,12 +196,8 @@ public class BundleWebAppProvider extends AbstractWebAppProvider implements Bund
if (bundle.getEntry("/WEB-INF/web.xml") != null)
{
String base = ".";
contextPath = getContextPath(bundle);
String originId = getOriginId(bundle, base);
OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
app.setContextPath(contextPath);
app.setWebAppPath(base);
OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle);
app.setPathToResourceBase(base);
_bundleMap.put(bundle, app);
getDeploymentManager().addApp(app);
return true;
@ -189,24 +208,16 @@ public class BundleWebAppProvider extends AbstractWebAppProvider implements Bund
{
//Could be a static webapp with no web.xml
String base = ".";
contextPath = headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
String originId = getOriginId(bundle, base);
OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
app.setContextPath(contextPath);
app.setWebAppPath(base);
OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle);
app.setPathToResourceBase(base);
_bundleMap.put(bundle, app);
getDeploymentManager().addApp(app);
return true;
}
//not a webapp
return false;
}
catch (Exception e)
{
throw e;
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
@ -230,28 +241,4 @@ public class BundleWebAppProvider extends AbstractWebAppProvider implements Bund
}
return false;
}
private static String getContextPath(Bundle bundle)
{
Dictionary<?, ?> headers = bundle.getHeaders();
String contextPath = (String)headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
if (contextPath == null)
{
// extract from the last token of the bundle's location:
// (really ?could consider processing the symbolic name as an alternative
// the location will often reflect the version.
// maybe this is relevant when the file is a war)
String location = bundle.getLocation();
String[] toks = StringUtil.replace(location, '\\', '/').split("/");
contextPath = toks[toks.length - 1];
// remove .jar, .war etc:
int lastDot = contextPath.lastIndexOf('.');
if (lastDot != -1)
contextPath = contextPath.substring(0, lastDot);
}
if (!contextPath.startsWith("/"))
contextPath = "/" + contextPath;
return contextPath;
}
}

View File

@ -11,19 +11,13 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot;
package org.eclipse.jetty.osgi;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.osgi.framework.ServiceReference;
/**
* ServiceProvider
*
* Jetty DeploymentManager Provider api for webapps or ContextHandlers that are discovered as OSGi services.
*/
public interface ServiceProvider
public interface ContextFactory
{
public boolean serviceAdded(ServiceReference ref, ContextHandler handler) throws Exception;
public boolean serviceRemoved(ServiceReference ref, ContextHandler handler) throws Exception;
ContextHandler createContextHandler(AbstractContextProvider provider, App app) throws Exception;
}

View File

@ -11,11 +11,13 @@
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory;
package org.eclipse.jetty.osgi;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Enumeration;
@ -24,32 +26,28 @@ import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.eclipse.jetty.ee10.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.ee10.osgi.boot.OSGiServerConstants;
import org.eclipse.jetty.ee10.osgi.boot.utils.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.ee10.osgi.boot.utils.OSGiClassLoader;
import org.eclipse.jetty.ee10.osgi.boot.utils.Util;
import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.util.OSGiClassLoader;
import org.eclipse.jetty.osgi.util.Util;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.resource.JarResource;
import org.eclipse.jetty.util.resource.Resource;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* DefaultJettyAtJettyHomeHelper
* JettyBootstrapActivator
* <p>
* Creates a default instance of Jetty, based on the values of the
* System properties "jetty.home" or "jetty.home.bundle", one of which
* must be specified in order to create the default instance.
* <p>
* Called by the {@link JettyBootstrapActivator} during the starting of the
* bundle.
* Bootstrap jetty and publish a default Server instance as an OSGi service.
*/
public class DefaultJettyAtJettyHomeHelper
public class JettyBootstrapActivator implements BundleActivator
{
private static final Logger LOG = LoggerFactory.getLogger(DefaultJettyAtJettyHomeHelper.class);
private static final Logger LOG = LoggerFactory.getLogger(JettyBootstrapActivator.class);
private static JettyBootstrapActivator INSTANCE = null;
/**
* contains a comma separated list of paths to the etc/jetty-*.xml files
@ -66,10 +64,73 @@ public class DefaultJettyAtJettyHomeHelper
*/
public static final String DEFAULT_JETTYHOME = "/jettyhome";
private ServiceRegistration<?> _registeredServer;
/* private PackageAdminServiceTracker _packageAdminServiceTracker;*/
/**
* Called by the JettyBootStrapActivator. If the system property jetty.home
* is defined and points to a folder, creates a corresponding jetty
* server.
* Setup a new jetty Server, register it as a service.
*
* @param context the bundle context
*/
@Override
public void start(final BundleContext context) throws Exception
{
// track other bundles and fragments attached to this bundle that we
// should activate, as OSGi will not call activators for them.
/* _packageAdminServiceTracker = new PackageAdminServiceTracker(context);*/
ServiceReference[] references = context.getAllServiceReferences("org.eclipse.jetty.http.HttpFieldPreEncoder", null);
if (references == null || references.length == 0)
LOG.warn("OSGi support for java.util.ServiceLoader may not be present. You may experience runtime errors.");
// Create a default jetty instance right now.
startJettyAtJettyHome(context);
}
/**
* Stop the activator.
*
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
@Override
public void stop(BundleContext context) throws Exception
{
/* if (_packageAdminServiceTracker != null)
{
_packageAdminServiceTracker.stop();
context.removeServiceListener(_packageAdminServiceTracker);
_packageAdminServiceTracker = null;
}
*/
try
{
if (_registeredServer != null)
{
try
{
_registeredServer.unregister();
}
catch (IllegalArgumentException ill)
{
// already unregistered.
}
finally
{
_registeredServer = null;
}
}
}
finally
{
INSTANCE = null;
}
}
/**
* If the system property jetty.home is defined and points to a folder,
* creates a corresponding jetty server.
* <p>
* If the system property jetty.home.bundle is defined and points to a
* bundle, look for the configuration of jetty inside that bundle.
@ -87,10 +148,9 @@ public class DefaultJettyAtJettyHomeHelper
* </p>
*
* @param bundleContext the bundle context
* @return the configured server
* @throws Exception if unable to create / configure / or start the server
*/
public static Server startJettyAtJettyHome(BundleContext bundleContext) throws Exception
private void startJettyAtJettyHome(BundleContext bundleContext) throws Exception
{
String jettyHomeSysProp = System.getProperty(OSGiServerConstants.JETTY_HOME);
String jettyHomeBundleSysProp = System.getProperty(OSGiServerConstants.JETTY_HOME_BUNDLE);
@ -112,7 +172,7 @@ public class DefaultJettyAtJettyHomeHelper
if (!jettyHomeDir.exists() || !jettyHomeDir.isDirectory())
{
LOG.warn("Unable to locate the jetty.home folder {}", jettyHomeSysProp);
return null;
return;
}
//set jetty.home
@ -135,14 +195,14 @@ public class DefaultJettyAtJettyHomeHelper
if (jettyHomeBundle == null)
{
LOG.warn("Unable to find the jetty.home.bundle named {}", jettyHomeSysProp);
return null;
return;
}
}
if (jettyHomeDir == null && jettyHomeBundle == null)
{
LOG.warn("No default jetty created.");
return null;
return;
}
//resolve the jetty xml config files
@ -196,15 +256,14 @@ public class DefaultJettyAtJettyHomeHelper
}
}
//configure the server here rather than letting the JettyServerServiceTracker do it, because we want to be able to
//configure the ThreadPool, which can only be done via the constructor, ie from within the xml configuration processing
Server server = ServerInstanceWrapper.configure(null, configURLs, properties);
//Create the default Server instance
Server defaultServer = JettyServerFactory.createServer(OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME, properties, configURLs);
//Register the default Server instance as an OSGi service.
//The JettyServerServiceTracker will notice it and set it up to deploy bundles as wars etc
bundleContext.registerService(Server.class.getName(), server, properties);
//The JettyServerServiceTrackers will notice it and set it up to deploy bundles as wars etc
//for each environment eg ee9,ee10, etc
_registeredServer = bundleContext.registerService(Server.class.getName(), defaultServer, properties);
LOG.info("Default jetty server configured");
return server;
}
catch (Exception e)
{
@ -223,7 +282,7 @@ public class DefaultJettyAtJettyHomeHelper
* look for the corresponding jetty configuration files that will be used to
* setup the jetty server.
*/
private static List<URL> getJettyConfigurationURLs(File jettyhome)
private List<URL> getJettyConfigurationURLs(File jettyhome)
throws MalformedURLException
{
List<URL> configURLs = new ArrayList<>();
@ -247,7 +306,7 @@ public class DefaultJettyAtJettyHomeHelper
* jetty.etc.config.urls and look for the corresponding jetty configuration
* files that will be used to setup the jetty server.
*/
private static List<URL> getJettyConfigurationURLs(Bundle configurationBundle, Dictionary properties)
private List<URL> getJettyConfigurationURLs(Bundle configurationBundle, Dictionary properties)
throws Exception
{
List<URL> configURLs = new ArrayList<>();
@ -279,9 +338,9 @@ public class DefaultJettyAtJettyHomeHelper
//lazily ensure jetty.home value is set based on location of etc files
if (properties.get(OSGiServerConstants.JETTY_HOME) == null)
{
Resource res = findDir(configurationBundle, home);
if (res != null)
properties.put(OSGiServerConstants.JETTY_HOME, res.toString());
Path path = findDir(configurationBundle, home);
if (path != null)
properties.put(OSGiServerConstants.JETTY_HOME, path.toUri().toString());
}
if (enUrls == null || !enUrls.hasMoreElements())
@ -295,14 +354,14 @@ public class DefaultJettyAtJettyHomeHelper
}
/**
* Get a resource representing a directory inside a bundle. If the dir is null,
* return a resource representing the installation location of the bundle.
* Resolve a directory inside a bundle. If the dir is null,
* return a path representing the installation location of the bundle.
*
* @param bundle the bundle
* @param dir the directory
* @return the resource found
* @return either the resolved dir inside the bundle, or the path of the bundle itself
*/
public static Resource findDir(Bundle bundle, String dir)
private Path findDir(Bundle bundle, String dir)
{
if (bundle == null)
return null;
@ -312,18 +371,12 @@ public class DefaultJettyAtJettyHomeHelper
File f = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle);
URL u = f.toURI().toURL();
u = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(u);
Resource res = Resource.newResource(u);
URI ruri = res.toURI();
Path p = Paths.get(u.toURI());
// check if it is an unarchived bundle
if ("file".equalsIgnoreCase(ruri.getScheme()) && FileID.isJavaArchive(ruri))
res = JarResource.newJarResource(res);
//if looking for a directory
if (dir != null)
res = res.addPath(dir);
return res;
return p.resolve(dir);
else
return p;
}
catch (Exception e)
{

View File

@ -0,0 +1,261 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.osgi;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.StringTokenizer;
import org.eclipse.jetty.deploy.AppLifeCycle;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.deploy.bindings.StandardStarter;
import org.eclipse.jetty.deploy.bindings.StandardStopper;
import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.util.Util;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.resource.ResourceFactory;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* JettyServerFactory
*
* Configures a jetty Server instance.
*/
public class JettyServerFactory
{
private static final Logger LOG = LoggerFactory.getLogger(JettyServerFactory.class.getName());
/**
* The value of this property points to the parent director of the jetty.xml
* configuration file currently executed. Everything is passed as a URL to
* support the case where the bundle is zipped.
*/
public static final String PROPERTY_THIS_JETTY_XML_FOLDER_URL = "this.jetty.xml.parent.folder.url";
/*
* Create a Server that is suitable for using in OSGi
*/
public static Server createServer(String name, Dictionary<String, Object> props, List<URL> jettyConfigurations)
throws Exception
{
Objects.requireNonNull(name);
Server server = null;
ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
try
{
List<URL> sharedURLs = getManagedJettySharedLibFolderUrls(props);
// Ensure we have a classloader that will have access to all jetty classes
ClassLoader libExtClassLoader = LibExtClassLoaderHelper.createLibExtClassLoader(null, sharedURLs, contextCl/*JettyServerFactory.class.getClassLoader()*/);
ClassLoader serverClassLoader = libExtClassLoader;
if (LOG.isDebugEnabled())
LOG.debug("LibExtClassLoader = {}", libExtClassLoader);
Thread.currentThread().setContextClassLoader(libExtClassLoader);
//Get ready to apply jetty configuration files, both those as explicit argument,
//as well as those provided by property
List<URL> jettyConfigs = new ArrayList<>();
if (jettyConfigurations != null)
jettyConfigs.addAll(jettyConfigurations);
//config files provided as part of the osgi properties
String jettyConfigFilenames = (String)props.get(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS);
if (jettyConfigFilenames != null)
{
jettyConfigs.addAll(Util.fileNamesAsURLs(jettyConfigFilenames, StringUtil.DEFAULT_DELIMS));
}
Map<String, Object> idMap = new HashMap<>();
Map<String, String> properties = new HashMap<>();
if (props != null)
{
Enumeration<String> en = props.keys();
while (en.hasMoreElements())
{
String key = en.nextElement();
Object value = props.get(key);
properties.put(key, value.toString());
}
}
try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable())
{
//create the server via config files
for (URL jettyConfiguration : jettyConfigurations)
{
try
{
// Execute a Jetty configuration file
XmlConfiguration config = new XmlConfiguration(resourceFactory.newResource(jettyConfiguration));
config.getIdMap().putAll(idMap);
config.getProperties().putAll(properties);
// #334062 compute the URL of the folder that contains the
// conf file and set it as a property so we can compute relative paths
// from it.
String urlPath = jettyConfiguration.toString();
int lastSlash = urlPath.lastIndexOf('/');
if (lastSlash > 4)
{
urlPath = urlPath.substring(0, lastSlash);
config.getProperties().put(PROPERTY_THIS_JETTY_XML_FOLDER_URL, urlPath);
}
Object o = config.configure();
server = (Server)o;
idMap = config.getIdMap();
}
catch (Exception e)
{
LOG.warn("Configuration error in {}", jettyConfiguration);
throw e;
}
}
}
//if no config files, create the server
if (server == null)
server = new Server();
//ensure ContextHandlerCollection
ContextHandlerCollection contextHandlerCollection = getContextHandlerCollection(server);
if (contextHandlerCollection == null)
{
contextHandlerCollection = new ContextHandlerCollection();
server.setHandler(contextHandlerCollection);
}
//ensure DeploymentManager
DeploymentManager deploymentManager = ensureDeploymentManager(server);
deploymentManager.setUseStandardBindings(false);
List<AppLifeCycle.Binding> deploymentLifeCycleBindings = new ArrayList<>();
deploymentLifeCycleBindings.add(new OSGiDeployer(server));
deploymentLifeCycleBindings.add(new StandardStarter());
deploymentLifeCycleBindings.add(new StandardStopper());
deploymentLifeCycleBindings.add(new OSGiUndeployer(server));
deploymentManager.setLifeCycleBindings(deploymentLifeCycleBindings);
server.setAttribute(OSGiServerConstants.JETTY_HOME, properties.get(OSGiServerConstants.JETTY_HOME));
server.setAttribute(OSGiServerConstants.JETTY_BASE, properties.get(OSGiServerConstants.JETTY_BASE));
server.setAttribute(OSGiServerConstants.SERVER_CLASSLOADER, serverClassLoader);
server.setAttribute(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, name);
return server;
}
catch (Exception e)
{
if (server != null)
{
try
{
server.stop();
}
catch (Exception x)
{
LOG.trace("IGNORED", x);
}
}
throw e;
}
finally
{
Thread.currentThread().setContextClassLoader(contextCl);
}
}
private static DeploymentManager ensureDeploymentManager(Server server)
{
Collection<DeploymentManager> deployers = server.getBeans(DeploymentManager.class);
DeploymentManager deploymentManager;
if (deployers != null)
{
deploymentManager = deployers.stream().findFirst().get();
}
else
{
deploymentManager = new DeploymentManager();
deploymentManager.setContexts(getContextHandlerCollection(server));
server.addBean(deploymentManager);
}
return deploymentManager;
}
private static ContextHandlerCollection getContextHandlerCollection(Server server)
{
return (ContextHandlerCollection)server.getDescendant(ContextHandlerCollection.class);
}
/**
* Get the Jetty Shared Lib Folder URLs in a form that is suitable for
* {@link LibExtClassLoaderHelper} to use.
*
* @param props the properties to look for the configuration in
* @return the list of URLs found, or null if none found
*/
private static List<URL> getManagedJettySharedLibFolderUrls(Dictionary<String, Object> props)
{
String sharedURLs = (String)props.get(OSGiServerConstants.MANAGED_JETTY_SHARED_LIB_FOLDER_URLS);
if (StringUtil.isBlank(sharedURLs))
{
return null;
}
List<URL> libURLs = new ArrayList<>();
StringTokenizer tokenizer = new StringTokenizer(sharedURLs, StringUtil.DEFAULT_DELIMS, false);
while (tokenizer.hasMoreTokens())
{
String tok = tokenizer.nextToken();
try
{
URL url = new URL(tok);
url = BundleFileLocatorHelperFactory.getFactory().getHelper().getFileURL(url);
if (url.getProtocol().equals("file"))
{
libURLs.add(new URL("jar:" + url.toExternalForm() + "!/"));
}
else
{
if (LOG.isDebugEnabled())
LOG.debug("Unrecognized Jetty Shared Lib URL: {}", url);
}
}
catch (Throwable mfe)
{
LOG.warn("Unable to process legacy lib folder {}", tok, mfe);
}
}
return libURLs;
}
}

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot.internal.webapp;
package org.eclipse.jetty.osgi;
import java.io.File;
import java.net.MalformedURLException;
@ -25,6 +25,8 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.eclipse.jetty.util.FileID;
/**
* LibExtClassLoaderHelper
* <p>
@ -50,7 +52,6 @@ import java.util.Set;
*/
public class LibExtClassLoaderHelper
{
/**
* IFilesInJettyHomeResourcesProcessor
*

View File

@ -0,0 +1,259 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.osgi;
import java.io.File;
import java.net.URI;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Objects;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.ee.Deployable;
import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.FileID;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* OSGiApp
*
* Base class representing info about a WebAppContext/ContextHandler to be deployed into jetty.
*/
public class OSGiApp extends App
{
private static final Logger LOG = LoggerFactory.getLogger(OSGiApp.class);
protected Bundle _bundle;
protected ServiceRegistration _registration;
protected ContextHandler _contextHandler;
protected String _pathToResourceBase;
protected String _contextPath;
protected Resource _bundleResource;
/**
* Get the install location of a Bundle as a Path
* @param bundle the Bundle whose location to return
* @return the installed location of the Bundle as a Path
* @throws Exception
*/
private static Path getBundlePath(Bundle bundle) throws Exception
{
String bundleOverrideLocation = bundle.getHeaders().get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE);
File bundleLocation = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle);
File root = (bundleOverrideLocation == null ? bundleLocation : new File(bundleOverrideLocation));
return Paths.get(root.toURI());
}
/**
* Convert a bundle installed location into a Resource, taking account of
* any locations that are actually packed jars, but without a ".jar" extension, eg
* as found on equinox. Eg file:///a/b/c/org.eclipse.osgi/89/0/bundleFile
* @param bundle the bundle
* @return a Resource representing the bundle's installed location
* @throws Exception
*/
private static Resource getBundleAsResource(Bundle bundle) throws Exception
{
String bundleOverrideLocation = bundle.getHeaders().get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE);
File bundleLocation = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle);
File root = (bundleOverrideLocation == null ? bundleLocation : new File(bundleOverrideLocation));
//Fix some osgiPaths.get( locations which point to an archive, but that doesn't end in .jar
URL url = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(root.toURI().toURL());
return ResourceFactory.root().newResource(url);
}
/**
* Get or create a contextPath from bundle headers and information
*
* @param bundle
* @return a contextPath
*/
private static String getContextPath(Bundle bundle)
{
Dictionary<?, ?> headers = bundle.getHeaders();
String contextPath = (String)headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
if (contextPath == null)
{
// extract from the last token of the bundle's location:
// (really ?could consider processing the symbolic name as an alternative
// the location will often reflect the version.
// maybe this is relevant when the file is a war)
String location = bundle.getLocation();
String[] toks = StringUtil.replace(location, '\\', '/').split("/");
contextPath = toks[toks.length - 1];
// remove .jar, .war etc:
int lastDot = contextPath.lastIndexOf('.');
if (lastDot != -1)
contextPath = contextPath.substring(0, lastDot);
}
if (!contextPath.startsWith("/"))
contextPath = "/" + contextPath;
return contextPath;
}
/**
* @param manager the DeploymentManager to which to deploy
* @param provider the provider that discovered the context/webapp
* @param bundle the bundle associated with the context/webapp
*/
public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle)
throws Exception
{
super(manager, provider, getBundlePath(bundle));
_bundle = Objects.requireNonNull(bundle);
_bundleResource = getBundleAsResource(bundle);
//copy selected bundle headers into the properties
Dictionary<String, String> headers = bundle.getHeaders();
Enumeration<String> keys = headers.keys();
while (keys.hasMoreElements())
{
String key = keys.nextElement();
String val = headers.get(key);
if (Deployable.ENVIRONMENT.equalsIgnoreCase(key) || OSGiWebappConstants.JETTY_ENVIRONMENT.equalsIgnoreCase(key))
getProperties().put(Deployable.ENVIRONMENT, val);
else if (Deployable.DEFAULTS_DESCRIPTOR.equalsIgnoreCase(key) || OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH.equalsIgnoreCase(key))
{
getProperties().put(Deployable.DEFAULTS_DESCRIPTOR, val);
}
else if (OSGiWebappConstants.JETTY_WEB_XML_PATH.equalsIgnoreCase(key))
{
getProperties().put(key, val);
}
else if (OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH.equalsIgnoreCase(key))
{
getProperties().put(key, val);
}
}
//set up the context path based on the supplied value, or the calculated default
setContextPath(getContextPath(bundle));
}
public Resource getBundleResource()
{
return _bundleResource;
}
@Override
public ContextHandler getContextHandler() throws Exception
{
if (_contextHandler == null)
_contextHandler = getAppProvider().createContextHandler(this);
return _contextHandler;
}
public void setContextHandler(ContextHandler contextHandler)
{
_contextHandler = contextHandler;
}
public String getPathToResourceBase()
{
return _pathToResourceBase;
}
public void setPathToResourceBase(String path)
{
_pathToResourceBase = path;
}
@Override
public String getContextPath()
{
return _contextPath;
}
public void setContextPath(String contextPath)
{
_contextPath = contextPath;
}
public String getBundleSymbolicName()
{
return _bundle.getSymbolicName();
}
public String getBundleVersionAsString()
{
if (_bundle.getVersion() == null)
return null;
return _bundle.getVersion().toString();
}
public Bundle getBundle()
{
return _bundle;
}
public void setRegistration(ServiceRegistration registration)
{
_registration = registration;
}
public ServiceRegistration getRegistration()
{
return _registration;
}
/**
* Register the Jetty deployed context/webapp as a service, as
* according to the OSGi Web Application Specification.
*
* @throws Exception
*/
public void registerAsOSGiService() throws Exception
{
if (_registration == null)
{
Dictionary<String, String> properties = new Hashtable<String, String>();
properties.put(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK);
if (getBundleSymbolicName() != null)
properties.put(OSGiWebappConstants.OSGI_WEB_SYMBOLICNAME, getBundleSymbolicName());
if (getBundleVersionAsString() != null)
properties.put(OSGiWebappConstants.OSGI_WEB_VERSION, getBundleVersionAsString());
properties.put(OSGiWebappConstants.OSGI_WEB_CONTEXTPATH, getContextPath());
ServiceRegistration rego = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(ContextHandler.class.getName(), getContextHandler(), properties);
setRegistration(rego);
}
}
protected void deregisterAsOSGiService() throws Exception
{
if (_registration == null)
return;
_registration.unregister();
_registration = null;
}
}

View File

@ -11,13 +11,13 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot;
package org.eclipse.jetty.osgi;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.bindings.StandardDeployer;
import org.eclipse.jetty.deploy.graph.Node;
import org.eclipse.jetty.ee9.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.ee9.osgi.boot.utils.EventSender;
import org.eclipse.jetty.osgi.util.EventSender;
import org.eclipse.jetty.server.Server;
/**
* OSGiDeployer
@ -28,9 +28,9 @@ import org.eclipse.jetty.ee9.osgi.boot.utils.EventSender;
public class OSGiDeployer extends StandardDeployer
{
private ServerInstanceWrapper _server;
private Server _server;
public OSGiDeployer(ServerInstanceWrapper server)
public OSGiDeployer(Server server)
{
_server = server;
}
@ -40,22 +40,22 @@ public class OSGiDeployer extends StandardDeployer
{
//TODO how to NOT send this event if its not a webapp:
//OSGi Enterprise Spec only wants an event sent if its a webapp bundle (ie not a ContextHandler)
if (!(app instanceof AbstractOSGiApp))
if (!(app instanceof OSGiApp))
{
doProcessBinding(node, app);
}
else
{
EventSender.getInstance().send(EventSender.DEPLOYING_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
EventSender.getInstance().send(EventSender.DEPLOYING_EVENT, ((OSGiApp)app).getBundle(), app.getContextPath());
try
{
doProcessBinding(node, app);
((AbstractOSGiApp)app).registerAsOSGiService();
EventSender.getInstance().send(EventSender.DEPLOYED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
((OSGiApp)app).registerAsOSGiService();
EventSender.getInstance().send(EventSender.DEPLOYED_EVENT, ((OSGiApp)app).getBundle(), app.getContextPath());
}
catch (Exception e)
{
EventSender.getInstance().send(EventSender.FAILED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
EventSender.getInstance().send(EventSender.FAILED_EVENT, ((OSGiApp)app).getBundle(), app.getContextPath());
throw e;
}
}
@ -64,7 +64,8 @@ public class OSGiDeployer extends StandardDeployer
protected void doProcessBinding(Node node, App app) throws Exception
{
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(_server.getParentClassLoaderForWebapps());
ClassLoader cl = (ClassLoader)_server.getAttribute(OSGiServerConstants.SERVER_CLASSLOADER);
Thread.currentThread().setContextClassLoader(cl);
try
{
super.processBinding(node, app);

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot;
package org.eclipse.jetty.osgi;
/**
* OSGiServerConstants
@ -80,4 +80,15 @@ public class OSGiServerConstants
* List of URLs to the folders where the legacy J2EE shared libraries are stored aka lib/ext, lib/jsp etc.
*/
public static final String MANAGED_JETTY_SHARED_LIB_FOLDER_URLS = "managedJettySharedLibFolderUrls";
/*
* Name of server attribute that stores a classloader suitable as the parent classloader for contexts
*/
public static final String SERVER_CLASSLOADER = "org.eclipse.jetty.osgi.server.classLoader";
/**
* Name of server attribute that stores a List of Bundles on the server classpath that must be scanned
*/
public static final String SERVER_CLASSPATH_BUNDLES = "org.eclipse.jetty.osgi.server.classpathBundles";
}

View File

@ -11,13 +11,13 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot;
package org.eclipse.jetty.osgi;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.bindings.StandardUndeployer;
import org.eclipse.jetty.deploy.graph.Node;
import org.eclipse.jetty.ee9.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.ee9.osgi.boot.utils.EventSender;
import org.eclipse.jetty.osgi.util.EventSender;
import org.eclipse.jetty.server.Server;
/**
* OSGiUndeployer
@ -27,9 +27,9 @@ import org.eclipse.jetty.ee9.osgi.boot.utils.EventSender;
*/
public class OSGiUndeployer extends StandardUndeployer
{
private ServerInstanceWrapper _server;
private Server _server;
public OSGiUndeployer(ServerInstanceWrapper server)
public OSGiUndeployer(Server server)
{
_server = server;
}
@ -37,9 +37,10 @@ public class OSGiUndeployer extends StandardUndeployer
@Override
public void processBinding(Node node, App app) throws Exception
{
EventSender.getInstance().send(EventSender.UNDEPLOYING_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
EventSender.getInstance().send(EventSender.UNDEPLOYING_EVENT, ((OSGiApp)app).getBundle(), app.getContextPath());
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(_server.getParentClassLoaderForWebapps());
ClassLoader cl = (ClassLoader)_server.getAttribute(OSGiServerConstants.SERVER_CLASSLOADER);
Thread.currentThread().setContextClassLoader(cl);
try
{
super.processBinding(node, app);
@ -48,7 +49,7 @@ public class OSGiUndeployer extends StandardUndeployer
{
Thread.currentThread().setContextClassLoader(old);
}
EventSender.getInstance().send(EventSender.UNDEPLOYED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
((AbstractOSGiApp)app).deregisterAsOSGiService();
EventSender.getInstance().send(EventSender.UNDEPLOYED_EVENT, ((OSGiApp)app).getBundle(), app.getContextPath());
((OSGiApp)app).deregisterAsOSGiService();
}
}

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot;
package org.eclipse.jetty.osgi;
/**
* OSGiWebappConstants
@ -70,7 +70,7 @@ public class OSGiWebappConstants
public static final String JETTY_OSGI_BUNDLE = "osgi-bundle";
/**
* List of relative pathes within the bundle to the jetty context files.
* List of relative paths within the bundle to the jetty context files.
*/
public static final String JETTY_CONTEXT_FILE_PATH = "Jetty-ContextFilePath";
@ -119,6 +119,11 @@ public class OSGiWebappConstants
*/
public static final String JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE = "Jetty-bundleInstall";
/**
* Property naming the environment (eg ee8/ee9/ee10 etc)
*/
public static final String JETTY_ENVIRONMENT = "Jetty-Environment";
/**
* Comma separated list of bundles that contain tld file used by the webapp.
*/

View File

@ -11,9 +11,8 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot.utils;
package org.eclipse.jetty.osgi.util;
import org.eclipse.jetty.ee9.osgi.boot.utils.internal.DefaultBundleClassLoaderHelper;
import org.osgi.framework.Bundle;
/**
@ -37,7 +36,7 @@ public interface BundleClassLoaderHelper
/**
* The name of the custom implementation for this interface in a fragment.
*/
public static final String CLASS_NAME = "org.eclipse.jetty.ee9.osgi.boot.utils.BundleClassLoaderHelperImpl";
public static final String CLASS_NAME = "org.eclipse.jetty.osgi.util.BundleClassLoaderHelperImpl";
/**
* The default instance supports felix and equinox

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot.utils;
package org.eclipse.jetty.osgi.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -11,13 +11,12 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot.utils;
package org.eclipse.jetty.osgi.util;
import java.io.File;
import java.net.URL;
import java.util.Enumeration;
import org.eclipse.jetty.ee9.osgi.boot.utils.internal.DefaultFileLocatorHelper;
import org.osgi.framework.Bundle;
/**
@ -32,7 +31,7 @@ public interface BundleFileLocatorHelper
/**
* The name of the custom implementation for this interface in a fragment.
*/
public static final String CLASS_NAME = "org.eclipse.jetty.ee9.osgi.boot.utils.FileLocatorHelperImpl";
public static final String CLASS_NAME = "org.eclipse.jetty.osgi.util.FileLocatorHelperImpl";
/**
* The default instance supports felix and equinox

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot.utils;
package org.eclipse.jetty.osgi.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -11,13 +11,12 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot.utils.internal;
package org.eclipse.jetty.osgi.util;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import org.eclipse.jetty.ee9.osgi.boot.utils.BundleClassLoaderHelper;
import org.osgi.framework.Bundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot.utils.internal;
package org.eclipse.jetty.osgi.util;
import java.io.File;
import java.lang.reflect.Field;
@ -20,14 +20,13 @@ import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.zip.ZipFile;
import org.eclipse.jetty.ee9.osgi.boot.utils.BundleFileLocatorHelper;
import org.eclipse.jetty.util.FileID;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.util.resource.Resource;
import org.osgi.framework.Bundle;
/**
@ -103,7 +102,7 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
// some osgi frameworks do use the file protocol directly in some
// situations. Do use the PathResource to transform the URL into a
// File: URL#toURI is broken
return new PathResource(url).getFile().getParentFile().getParentFile().getCanonicalFile();
return Paths.get(url.toURI()).getParent().getParent().toFile();
}
else if (url.getProtocol().equals("bundleentry"))
{
@ -112,7 +111,6 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
// the File
URLConnection con = url.openConnection();
con.setUseCaches(Resource.getDefaultUseCaches()); // work around
// problems where
// url connections
// cache
@ -141,7 +139,6 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
url = bundle.getEntry("/");
con = url.openConnection();
con.setDefaultUseCaches(Resource.getDefaultUseCaches());
if (BUNDLE_ENTRY_FIELD == null)
{
@ -334,7 +331,6 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
{
URLConnection conn = url.openConnection();
conn.setDefaultUseCaches(Resource.getDefaultUseCaches());
if (BUNDLE_URL_CONNECTION_getLocalURL == null && match(conn.getClass().getName(), BUNDLE_URL_CONNECTION_CLASSES))
{
BUNDLE_URL_CONNECTION_getLocalURL = conn.getClass().getMethod("getLocalURL");
@ -369,7 +365,6 @@ public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
{
URLConnection conn = url.openConnection();
conn.setDefaultUseCaches(Resource.getDefaultUseCaches());
if (BUNDLE_URL_CONNECTION_getFileURL == null && match(conn.getClass().getName(), BUNDLE_URL_CONNECTION_CLASSES))
{
BUNDLE_URL_CONNECTION_getFileURL = conn.getClass().getMethod("getFileURL");

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot.utils;
package org.eclipse.jetty.osgi.util;
import java.util.Dictionary;
import java.util.Hashtable;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot.utils;
package org.eclipse.jetty.osgi.util;
import java.net.URL;
import java.net.URLClassLoader;

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot.utils;
package org.eclipse.jetty.osgi.util;
import java.io.IOException;
import java.net.URL;

View File

@ -11,18 +11,19 @@
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot;
package org.eclipse.jetty.osgi.util;
import java.util.List;
import org.osgi.framework.Bundle;
/**
* BundleProvider
*
* Jetty DeploymentManager Provider api for webapps or ContextHandlers that are discovered as osgi bundles.
*/
public interface BundleProvider
public interface ServerClasspathContributor
{
public boolean bundleAdded(Bundle bundle) throws Exception;
public boolean bundleRemoved(Bundle bundle) throws Exception;
/**
* Get bundles that should be on the Server classpath,
* and should be scanned for annotations/tlds/resources etc
*
* @return list of Bundles to be scanned and put on server classpath
*/
List<Bundle> getScannableBundles();
}

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot.utils;
package org.eclipse.jetty.osgi.util;
import java.io.FileWriter;
import java.nio.file.Files;

View File

@ -11,25 +11,138 @@
// ========================================================================
//
package org.eclipse.jetty.ee9.osgi.boot.utils;
package org.eclipse.jetty.osgi.util;
import java.io.File;
import java.net.URI;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Dictionary;
import java.util.List;
import java.util.Objects;
import java.util.StringTokenizer;
import org.eclipse.jetty.ee9.osgi.boot.OSGiServerConstants;
import org.eclipse.jetty.osgi.OSGiServerConstants;
import org.eclipse.jetty.util.StringUtil;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.packageadmin.PackageAdmin;
/**
* Various useful functions utility methods for OSGi wide use.
*/
public class Util
{
/**
* Resolve a path either absolutely or against the bundle install location, or
* against jetty home.
*
* @param path the path to resolve
* @param bundle the bundle
* @param jettyHome the path to jetty home
* @return the URI within the bundle as a usable URI
*/
public static URI resolvePathAsLocalizedURI(String path, Bundle bundle, Path jettyHome)
throws Exception
{
if (StringUtil.isBlank(path))
return null;
if (path.startsWith("/") || path.startsWith("file:/")) //absolute location
return Paths.get(path).toUri();
//relative location
//try inside the bundle first
if (bundle != null)
{
URL url = bundle.getEntry(path);
if (url != null)
{
return BundleFileLocatorHelper.DEFAULT.getLocalURL(url).toURI();
}
}
//try resolving against jetty.home
if (jettyHome != null)
{
Path p = jettyHome.resolve(path);
if (Files.exists(p))
return p.toUri();
}
return null;
}
public static URL getLocalURL(URL url)
throws Exception
{
if (url == null)
return null;
return BundleFileLocatorHelper.DEFAULT.getLocalURL(url);
}
public static URL getLocalizedEntry(String file, Bundle bundle)
throws Exception
{
if (file == null || bundle == null)
return null;
URL url = bundle.getEntry(file);
if (url == null)
return null;
return BundleFileLocatorHelper.DEFAULT.getLocalURL(url);
}
/**
* Resolve the file system paths to bundles identified by their symbolic names.
*
* @param bundleSymbolicNames comma separated list of symbolic bundle names
* @param bundleContext the bundle on whose behalf to resolve
* @return List of resolved Paths matching the bundle symbolic names
*
* @throws Exception
*/
public static List<Path> getPathsToBundlesBySymbolicNames(String bundleSymbolicNames, BundleContext bundleContext)
throws Exception
{
if (bundleSymbolicNames == null)
return Collections.emptyList();
Objects.requireNonNull(bundleContext);
ServiceReference ref = bundleContext.getServiceReference(org.osgi.service.packageadmin.PackageAdmin.class.getName());
PackageAdmin packageAdmin = (ref == null) ? null : (PackageAdmin)bundleContext.getService(ref);
if (packageAdmin == null)
throw new IllegalStateException("Unable to get PackageAdmin reference to locate required Tld bundles");
List<Path> paths = new ArrayList<>();
String[] symbNames = bundleSymbolicNames.split("[, ]");
for (String symbName : symbNames)
{
Bundle[] bs = packageAdmin.getBundles(symbName, null);
if (bs == null || bs.length == 0)
{
throw new IllegalArgumentException("Unable to locate the bundle '" + symbName + "' specified in manifest of " +
bundleContext.getBundle().getSymbolicName());
}
File f = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bs[0]);
paths.add(f.toPath());
}
return paths;
}
/**
* Create an osgi filter for the given classname and server name.
*

View File

@ -28,6 +28,7 @@
<module>jetty-jmx</module>
<module>jetty-jndi</module>
<module>jetty-keystore</module>
<module>jetty-osgi</module>
<module>jetty-proxy</module>
<module>jetty-quic</module>
<module>jetty-rewrite</module>
@ -51,11 +52,6 @@
<artifactId>jetty-jakarta-servlet-api</artifactId>
<version>5.0.2</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee9</groupId>
<artifactId>jetty-ee9-webapp</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@ -578,7 +578,7 @@ public class AnnotationParser
* @param dirResource the resource representing the baseResource being scanned (jar, dir, etc)
* @throws Exception if unable to parse
*/
private void parseDir(Set<? extends Handler> handlers, Resource dirResource) throws Exception
protected void parseDir(Set<? extends Handler> handlers, Resource dirResource) throws Exception
{
Path dir = dirResource.getPath();
@ -617,13 +617,13 @@ public class AnnotationParser
* @param jarResource the jar resource to parse
* @throws Exception if unable to parse
*/
private void parseJar(Set<? extends Handler> handlers, Resource jarResource) throws Exception
protected void parseJar(Set<? extends Handler> handlers, Resource jarResource) throws Exception
{
if (jarResource == null)
return;
if (!FileID.isJavaArchive(jarResource.getPath()))
return;
/* if (!FileID.isJavaArchive(jarResource.getPath()))
return;*/
if (LOG.isDebugEnabled())
LOG.debug("Scanning jar {}", jarResource);
@ -643,7 +643,7 @@ public class AnnotationParser
* @param classFile the class file to parse
* @throws IOException if unable to parse
*/
private void parseClass(Set<? extends Handler> handlers, Resource containingResource, Path classFile) throws IOException
protected void parseClass(Set<? extends Handler> handlers, Resource containingResource, Path classFile) throws IOException
{
if (LOG.isDebugEnabled())
LOG.debug("Parse class from {}", classFile.toUri());

View File

@ -24,7 +24,7 @@
<instructions>
<Bundle-Description>Jetty-specific ServletContainerInitializer for Jasper</Bundle-Description>
<Export-Package>
org.eclipse.jetty.ee10.apache.jsp.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}", org.eclipse.jetty.jsp.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"
org.eclipse.jetty.ee10.apache.jsp.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}", org.eclipse.jetty.ee10.jsp.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"
</Export-Package>
<Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional
</Require-Capability>

View File

@ -64,13 +64,14 @@
<Export-Package>!org.example*</Export-Package>
<!-- the test webapp is configured via a jetty xml file
in order to add the security handler. -->
<Web-ContextPath>/</Web-ContextPath>
<Web-ContextPath>/ee10-demo-jetty</Web-ContextPath>
<!-- in fact the '.' must not be there
but Felix-BND has a bug:
http://www.mail-archive.com/users@felix.apache.org/msg04730.html
https://issues.apache.org/jira/browse/FELIX-1571
-->
<Bundle-ClassPath>.,WEB-INF/classes</Bundle-ClassPath>
<Jetty-Environment>ee10</Jetty-Environment>
</instructions>
</configuration>
</plugin>

View File

@ -28,8 +28,9 @@
<instructions>
<Import-Package>jakarta.servlet.jsp.*;version="[3,4)",org.eclipse.jetty.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))",*</Import-Package>
<Export-Package>!org.example.*</Export-Package>
<Web-ContextPath>/demo-jsp</Web-ContextPath>
<Web-ContextPath>/ee10-demo-jsp</Web-ContextPath>
<Bundle-ClassPath>.,WEB-INF/classes</Bundle-ClassPath>
<Jetty-Environment>ee10</Jetty-Environment>
</instructions>
</configuration>
</plugin>

View File

@ -70,9 +70,10 @@
</Import-Package>
<_nouses />
<Export-Package>org.example.test;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}";-noimport:=true</Export-Package>
<Web-ContextPath>/</Web-ContextPath>
<Web-ContextPath>/ee10-demo-spec</Web-ContextPath>
<Bundle-ClassPath>.,WEB-INF/classes,WEB-INF/lib</Bundle-ClassPath>
<Jetty-ContextFilePath>/META-INF/plugin-context.xml</Jetty-ContextFilePath>
<Jetty-ContextFilePath>META-INF/plugin-context.xml</Jetty-ContextFilePath>
<Jetty-Environment>ee10</Jetty-Environment>
</instructions>
</configuration>
</plugin>

View File

@ -16,12 +16,12 @@
</New>
<!-- ContextPath set in web.xml -->
<Set name="war"><Property name="jetty.webapps"/>/test-spec.war</Set>
<Set name="war"><Property name="jetty.webapps"/>/ee10-demo-spec.war</Set>
<Set name="configurationDiscovered">true</Set>
<Get name="securityHandler">
<Set name="loginService">
<New class="org.eclipse.jetty.security.HashLoginService">
<New class="org.eclipse.jetty.ee10.servlet.security.HashLoginService">
<Set name="name">Test Realm</Set>
<Set name="config"><SystemProperty name="jetty.base" default="."/>/etc/realm.properties</Set>
</New>

View File

@ -1,5 +1,5 @@
<New id="maxAmount" class="org.eclipse.jetty.plus.jndi.EnvEntry">
<New id="maxAmount" class="org.eclipse.jetty.ee10.plus.jndi.EnvEntry">
<Arg><Ref refid='wac'/></Arg>
<Arg>maxAmount</Arg>
<Arg type="java.lang.Double">100</Arg>
@ -7,7 +7,7 @@
</New>
<New id="mydatasource" class="org.eclipse.jetty.plus.jndi.Resource">
<New id="mydatasource" class="org.eclipse.jetty.ee10.plus.jndi.Resource">
<Arg><Ref refid='wac'/></Arg>
<Arg>jdbc/mydatasource</Arg>
<Arg>

View File

@ -10,7 +10,7 @@
<description>Jetty OSGi Boot JSP bundle</description>
<properties>
<bundle-symbolic-name>${project.groupId}.boot.jsp</bundle-symbolic-name>
<spotbugs.onlyAnalyze>org.eclipse.jetty.ee10.osgi.boot.jasper.*,org.eclipse.jetty.ee10.osgi.boot.jsp.*</spotbugs.onlyAnalyze>
<spotbugs.onlyAnalyze>org.eclipse.jetty.ee10.osgi.boot.jsp.*</spotbugs.onlyAnalyze>
</properties>
<dependencies>
<dependency>
@ -73,7 +73,6 @@
<Bundle-Name>Jetty-OSGi-Jasper Integration</Bundle-Name>
<Bundle-Classpath />
<Fragment-Host>org.eclipse.jetty.ee10.osgi.boot</Fragment-Host>
<Export-Package>!org.eclipse.jetty.ee10.osgi.boot.*</Export-Package>
<Import-Package>
${osgi.slf4j.import.packages},
org.eclipse.jdt.*;resolution:=optional,
@ -82,9 +81,9 @@
com.sun.el.lang;resolution:=optional,
com.sun.el.parser;resolution:=optional,
com.sun.el.util;resolution:=optional,
jakarta.el;version="[4.0,5.0)",
jakarta.servlet;version="[$(version;==;${servletImpl.osgiVersion}),$(version;+;${servletImpl.osgiVersion}))",
jakarta.servlet.resources;version="[$(version;==;${servletImpl.osgiVersion}),$(version;+;${servletImpl.osgiVersion}))",
jakarta.el;version="[5.0,6.0)",
jakarta.servlet;version="[$(version;==;${jakarta.servlet.api.version}),$(version;+;${jakarta.servlet.api.version}))",
jakarta.servlet.resources;version="[$(version;==;${jakarta.servlet.api.version}),$(version;+;${jakarta.servlet.api.version}))",
jakarta.servlet.jsp.resources;version="[$(version;==;${jakarta.servlet.jsp.api.version}),$(version;+;${jakarta.servlet.jsp.api.version}))",
jakarta.servlet.jsp;version="[$(version;==;${jakarta.servlet.jsp.api.version}),$(version;+;${jakarta.servlet.jsp.api.version}))",
jakarta.servlet.jsp.el;version="[$(version;==;${jakarta.servlet.jsp.api.version}),$(version;+;${jakarta.servlet.jsp.api.version}))",
@ -100,6 +99,7 @@
org.apache.el.parser;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional,
org.apache.jasper;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional,
org.apache.jasper.compiler;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional,
org.apache.jasper.compiler.util;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional,
org.apache.jasper.compiler.tagplugin;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional,
org.apache.jasper.runtime;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional,
org.apache.jasper.security;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional,
@ -131,7 +131,7 @@
org.apache.taglibs.standard.tei;version="2.0";resolution:=optional,
org.apache.taglibs.standard.tlv;version="2.0";resolution:=optional,
org.apache.tomcat;version="[10,11)";resolution:=optional,
org.eclipse.jetty.jsp;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))";resolution:=optional,
org.eclipse.jetty.ee10.jsp;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))";resolution:=optional,
org.osgi.*,
org.xml.*;resolution:=optional,
org.xml.sax.*;resolution:=optional,
@ -140,7 +140,7 @@
org.w3c.dom.ls;resolution:=optional,
javax.xml.parser;resolution:=optional
</Import-Package>
<DynamicImport-Package>org.eclipse.jetty.jsp.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))",
<DynamicImport-Package>org.eclipse.jetty.ee10.jsp.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))",
org.apache.jasper.*;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))",
org.apache.el.*;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))"
</DynamicImport-Package>

View File

@ -1,249 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.jasper;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import jakarta.servlet.jsp.JspFactory;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.ee10.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.ee10.osgi.boot.OSGiMetaInfConfiguration;
import org.eclipse.jetty.ee10.osgi.boot.utils.BundleFileLocatorHelper;
import org.eclipse.jetty.ee10.osgi.boot.utils.TldBundleDiscoverer;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ContainerTldBundleDiscoverer
*
* Finds bundles that are considered as on the container classpath that
* contain tlds.
*
* The System property org.eclipse.jetty.ee10.osgi.tldbundles is a comma
* separated list of exact symbolic names of bundles that have container classpath
* tlds.
*
* The DeploymentManager context attribute "org.eclipse.jetty.server.webapp.containerIncludeBundlePattern"
* can be used to define a pattern of symbolic names of bundles that contain container
* classpath tlds.
*
* The matching bundles are converted to URLs that are put onto a special classloader that acts as the
* parent classloader for contexts deployed by the jetty Server instance (see ServerInstanceWrapper).
*
* It also discovers the bundle that contains the jstl taglib and adds it into the
* "org.eclipse.jetty.server.webapp.containerIncludeBundlePattern" (if it is not already there) so
* that the WebInfOSGiConfiguration class will add the jstl taglib bundle into the list of container
* resources.
*
* Eg:
* -Dorg.eclipse.jetty.ee10.osgi.tldbundles=org.springframework.web.servlet,com.opensymphony.module.sitemesh
*/
public class ContainerTldBundleDiscoverer implements TldBundleDiscoverer
{
private static final Logger LOG = LoggerFactory.getLogger(ContainerTldBundleDiscoverer.class);
private static String DEFAULT_JSP_FACTORY_IMPL_CLASS = "org.apache.jasper.runtime.JspFactoryImpl";
/**
* Default name of a class that belongs to the jstl bundle. From that class
* we locate the corresponding bundle and register it as a bundle that
* contains tld files.
*/
private static String DEFAULT_JSTL_BUNDLE_CLASS = "org.apache.taglibs.standard.tag.rt.core.WhenTag";
private Bundle jstlBundle = null;
/**
* Check the System property "org.eclipse.jetty.ee10.osgi.tldbundles" for names of
* bundles that contain tlds and convert to URLs.
*
* @return The location of the jars that contain tld files as URLs.
*/
@Override
public URL[] getUrlsForBundlesWithTlds(DeploymentManager deploymentManager, BundleFileLocatorHelper locatorHelper) throws Exception
{
if (!isJspAvailable())
{
return new URL[0];
}
if (jstlBundle == null)
jstlBundle = findJstlBundle();
final Bundle[] bundles = FrameworkUtil.getBundle(ContainerTldBundleDiscoverer.class).getBundleContext().getBundles();
HashSet<URL> urls = new HashSet<URL>();
String tmp = System.getProperty(OSGiMetaInfConfiguration.SYS_PROP_TLD_BUNDLES); //comma separated exact names
List<String> sysNames = new ArrayList<String>();
if (tmp != null)
{
StringTokenizer tokenizer = new StringTokenizer(tmp, ", \n\r\t", false);
while (tokenizer.hasMoreTokens())
{
sysNames.add(tokenizer.nextToken());
}
}
tmp = (String)deploymentManager.getContextAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN); //bundle name patterns
Pattern pattern = (tmp == null ? null : Pattern.compile(tmp));
//check that the jstl bundle is not already included in the pattern, and include it if it is not because
//subsequent classes such as OSGiWebInfConfiguration use this pattern to determine which jars are
//considered to be on the container classpath
if (jstlBundle != null)
{
if (pattern == null)
{
pattern = Pattern.compile(jstlBundle.getSymbolicName());
deploymentManager.setContextAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, jstlBundle.getSymbolicName());
}
else if (!(pattern.matcher(jstlBundle.getSymbolicName()).matches()))
{
String s = tmp + "|" + jstlBundle.getSymbolicName();
pattern = Pattern.compile(s);
deploymentManager.setContextAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, s);
}
}
for (Bundle bundle : bundles)
{
if (sysNames.contains(bundle.getSymbolicName()))
convertBundleLocationToURL(locatorHelper, bundle, urls);
else if (pattern != null && pattern.matcher(bundle.getSymbolicName()).matches())
convertBundleLocationToURL(locatorHelper, bundle, urls);
}
return urls.toArray(new URL[urls.size()]);
}
/**
* Check that jsp is on the classpath
*
* @return <code>true</code> if jsp is available in the environment
*/
public boolean isJspAvailable()
{
try
{
getClass().getClassLoader().loadClass("org.apache.jasper.servlet.JspServlet");
}
catch (Exception e)
{
LOG.warn("Unable to locate the JspServlet: jsp support unavailable.", e);
return false;
}
return true;
}
/**
* Some versions of JspFactory do Class.forName, which probably won't work in an
* OSGi environment.
*/
public void fixJspFactory()
{
try
{
Class<jakarta.servlet.ServletContext> servletContextClass = jakarta.servlet.ServletContext.class;
// bug #299733
JspFactory fact = JspFactory.getDefaultFactory();
if (fact == null)
{ // bug #299733
// JspFactory does a simple
// Class.getForName("org.apache.jasper.runtime.JspFactoryImpl")
// however its bundles does not import the jasper package
// so it fails. let's help things out:
fact = (JspFactory)JettyBootstrapActivator.class.getClassLoader()
.loadClass(DEFAULT_JSP_FACTORY_IMPL_CLASS).getDeclaredConstructor().newInstance();
JspFactory.setDefaultFactory(fact);
}
}
catch (Exception e)
{
LOG.warn("Unable to set the JspFactory: jsp support incomplete.", e);
}
}
/**
* Find the bundle that contains a jstl implementation class, which assumes that
* the jstl taglibs will be inside the same bundle.
*
* @return Bundle contains the jstl implementation class
*/
public Bundle findJstlBundle()
{
Class<?> jstlClass = null;
try
{
jstlClass = JSTLBundleDiscoverer.class.getClassLoader().loadClass(DEFAULT_JSTL_BUNDLE_CLASS);
}
catch (ClassNotFoundException e)
{
LOG.info("jstl not on classpath", e);
}
if (jstlClass != null)
//get the bundle containing jstl
return FrameworkUtil.getBundle(jstlClass);
return null;
}
/**
* Resolves a bundle that contains tld files as a URL. The URLs are
* used by jasper to discover the tld files.
*
* Support only 2 types of packaging for the bundle: - the bundle is a jar
* (recommended for runtime.) - the bundle is a folder and contain jars in
* the root and/or in the lib folder (nice for PDE development situations)
* Unsupported: the bundle is a jar that embeds more jars.
*/
private void convertBundleLocationToURL(BundleFileLocatorHelper locatorHelper, Bundle bundle, Set<URL> urls) throws Exception
{
File jasperLocation = locatorHelper.getBundleInstallLocation(bundle);
if (jasperLocation.isDirectory())
{
for (File f : jasperLocation.listFiles())
{
if (FileID.isJavaArchive(f.getName()) && f.isFile())
{
urls.add(f.toURI().toURL());
}
else if (f.isDirectory() && f.getName().equals("lib"))
{
for (File f2 : jasperLocation.listFiles())
{
if (FileID.isJavaArchive(f2.getName()) && f2.isFile())
{
urls.add(f2.toURI().toURL());
}
}
}
}
urls.add(jasperLocation.toURI().toURL());
}
else
{
urls.add(jasperLocation.toURI().toURL());
}
}
}

View File

@ -1,178 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.jasper;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import jakarta.servlet.jsp.JspFactory;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.ee10.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.ee10.osgi.boot.utils.BundleFileLocatorHelper;
import org.eclipse.jetty.ee10.osgi.boot.utils.TldBundleDiscoverer;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* JSTLBundleDiscoverer
*
* Fix various shortcomings with the way jasper parses the tld files. Plugs the
* JSTL tlds assuming that they are packaged with the bundle that contains the
* JSTL classes.
* <p>
* Pluggable tlds at the server level are handled by
* {@link ContainerTldBundleDiscoverer}.
* </p>
*/
public class JSTLBundleDiscoverer implements TldBundleDiscoverer
{
private static final Logger LOG = LoggerFactory.getLogger(JSTLBundleDiscoverer.class);
/**
* Default name of a class that belongs to the jstl bundle. From that class
* we locate the corresponding bundle and register it as a bundle that
* contains tld files.
*/
private static String DEFAULT_JSTL_BUNDLE_CLASS = "org.apache.taglibs.standard.tag.el.core.WhenTag";
/**
* Default jsp factory implementation. Idally jasper is osgified and we can
* use services. In the mean time we statically set the jsp factory
* implementation. bug #299733
*/
private static String DEFAULT_JSP_FACTORY_IMPL_CLASS = "org.apache.jasper.runtime.JspFactoryImpl";
private static final Set<URL> __tldBundleCache = new HashSet<URL>();
public JSTLBundleDiscoverer()
{
try
{
// sanity check:
Class cl = getClass().getClassLoader().loadClass("org.apache.jasper.servlet.JspServlet");
}
catch (Exception e)
{
LOG.warn("Unable to locate the JspServlet: jsp support unavailable.", e);
return;
}
try
{
Class<jakarta.servlet.ServletContext> servletContextClass = jakarta.servlet.ServletContext.class;
// bug #299733
JspFactory fact = JspFactory.getDefaultFactory();
if (fact == null)
{ // bug #299733
// JspFactory does a simple
// Class.getForName("org.apache.jasper.runtime.JspFactoryImpl")
// however its bundles does not import the jasper package
// so it fails. let's help things out:
fact = (JspFactory)JettyBootstrapActivator.class.getClassLoader()
.loadClass(DEFAULT_JSP_FACTORY_IMPL_CLASS).getDeclaredConstructor().newInstance();
JspFactory.setDefaultFactory(fact);
}
}
catch (Exception e)
{
LOG.warn("Unable to set the JspFactory: jsp support incomplete.", e);
}
}
/**
* The jasper TldScanner expects a URLClassloader to parse a jar for the
* /META-INF/*.tld it may contain. We place the bundles that we know contain
* such tag-libraries. Please note that it will work if and only if the
* bundle is a jar (!) Currently we just hardcode the bundle that contains
* the jstl implemenation.
*
* A workaround when the tld cannot be parsed with this method is to copy
* and paste it inside the WEB-INF of the webapplication where it is used.
*
* Support only 2 types of packaging for the bundle: - the bundle is a jar
* (recommended for runtime.) - the bundle is a folder and contain jars in
* the root and/or in the lib folder (nice for PDE development situations)
* Unsupported: the bundle is a jar that embeds more jars.
*
* @return array of URLs
* @throws Exception In case of errors during resolving TLDs files
*/
@Override
public URL[] getUrlsForBundlesWithTlds(DeploymentManager deployer, BundleFileLocatorHelper locatorHelper) throws Exception
{
ArrayList<URL> urls = new ArrayList<URL>();
Class<?> jstlClass = null;
// Look for the jstl bundle
// We assume the jstl's tlds are defined there.
// We assume that the jstl bundle is imported by this bundle
// So we can look for this class using this bundle's classloader:
try
{
jstlClass = JSTLBundleDiscoverer.class.getClassLoader().loadClass(DEFAULT_JSTL_BUNDLE_CLASS);
}
catch (ClassNotFoundException e)
{
LOG.info("jstl not on classpath", e);
}
if (jstlClass != null)
{
//get the bundle containing jstl
Bundle tldBundle = FrameworkUtil.getBundle(jstlClass);
File tldBundleLocation = locatorHelper.getBundleInstallLocation(tldBundle);
if (tldBundleLocation != null && tldBundleLocation.isDirectory())
{
// try to find the jar files inside this folder
for (File f : tldBundleLocation.listFiles())
{
if (FileID.isJavaArchive(f.getName()) && f.isFile())
{
urls.add(f.toURI().toURL());
}
else if (f.isDirectory() && f.getName().equals("lib"))
{
for (File f2 : tldBundleLocation.listFiles())
{
if (FileID.isJavaArchive(f2.getName()) && f2.isFile())
{
urls.add(f2.toURI().toURL());
}
}
}
}
}
else if (tldBundleLocation != null)
{
urls.add(tldBundleLocation.toURI().toURL());
String pattern = (String)deployer.getContextAttribute("org.eclipse.jetty.server.webapp.containerIncludeBundlePattern");
pattern = (pattern == null ? "" : pattern);
if (!pattern.contains(tldBundle.getSymbolicName()))
{
pattern += "|" + tldBundle.getSymbolicName();
deployer.setContextAttribute("org.eclipse.jetty.server.webapp.containerIncludeBundlePattern", pattern);
}
}
}
return urls.toArray(new URL[urls.size()]);
}
}

View File

@ -13,8 +13,8 @@
package org.eclipse.jetty.ee10.osgi.boot.jsp;
import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.ee10.osgi.boot.jasper.ContainerTldBundleDiscoverer;
import org.eclipse.jetty.ee10.osgi.boot.EE10Activator;
import org.eclipse.jetty.osgi.util.ServerClasspathContributor;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
@ -24,10 +24,9 @@ import org.osgi.framework.BundleContext;
* Sets up support for jsp and jstl. All relevant jsp jars must also be installed
* into the osgi environment.
* <p>
* Note that as this is part of a bundle fragment, this activator is NOT
* called by the OSGi environment. Instead, the org.eclipse.jetty.ee10.osgi.boot.utils.internal.PackageAdminTracker
* simulates fragment activation and causes this class's start() method to
* be called.
* NOTE that as this is part of a bundle fragment, this activator is NOT
* called by the OSGi environment. Instead, the org.eclipse.jetty.osgi.util.internal.PackageAdminTracker
* simulates fragment activation and causes this class's start() method to be called.
* </p>
* <p>
* The package of this class MUST match the Bundle-SymbolicName of this fragment
@ -36,24 +35,21 @@ import org.osgi.framework.BundleContext;
*/
public class FragmentActivator implements BundleActivator
{
/**
*
*/
ServerClasspathContributor _tldClasspathContributor;
@Override
public void start(BundleContext context) throws Exception
{
//set up some classes that will look for bundles with tlds that must be converted
//to urls and treated as if they are on the Jetty container's classpath so that
//jasper can deal with them
ServerInstanceWrapper.addContainerTldBundleDiscoverer(new ContainerTldBundleDiscoverer());
//Register a class that will provide the identity of bundles that
//contain TLDs and therefore need to be scanned.
_tldClasspathContributor = new TLDServerClasspathContributor();
EE10Activator.registerServerClasspathContributor(_tldClasspathContributor);
}
/**
*
*/
@Override
public void stop(BundleContext context) throws Exception
{
EE10Activator.unregisterServerClasspathContributor(_tldClasspathContributor);
_tldClasspathContributor = null;
}
}

View File

@ -0,0 +1,112 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.jsp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.eclipse.jetty.ee10.osgi.boot.OSGiMetaInfConfiguration;
import org.eclipse.jetty.osgi.util.ServerClasspathContributor;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
/**
* @author janb
*
*/
public class TLDServerClasspathContributor implements ServerClasspathContributor
{
/**
* Name of a class that belongs to the jstl bundle. From that class
* we locate the corresponding bundle.
*/
private static String JSTL_BUNDLE_CLASS = "org.apache.taglibs.standard.tag.el.core.WhenTag";
@Override
public List<Bundle> getScannableBundles()
{
if (!isJspAvailable())
{
return Collections.emptyList();
}
List<Bundle> scannableBundles = new ArrayList<>();
List<String> bundleNames = Collections.emptyList();
String tmp = System.getProperty(OSGiMetaInfConfiguration.SYS_PROP_TLD_BUNDLES); //comma separated exact names
if (tmp != null)
{
String[] names = tmp.split(", \n\r\t");
bundleNames = Arrays.asList(names);
}
Bundle jstlBundle = findJstlBundle();
if (jstlBundle != null)
scannableBundles.add(jstlBundle);
final Bundle[] bundles = FrameworkUtil.getBundle(getClass()).getBundleContext().getBundles();
for (Bundle bundle : bundles)
{
if (bundleNames.contains(bundle.getSymbolicName()))
scannableBundles.add(bundle);
}
return scannableBundles;
}
/**
* Check that jsp is on the classpath
*
* @return <code>true</code> if jsp is available in the environment
*/
public boolean isJspAvailable()
{
try
{
getClass().getClassLoader().loadClass("org.apache.jasper.servlet.JspServlet");
return true;
}
catch (Exception e)
{
return false;
}
}
/**
* Find the bundle that contains a jstl implementation class, which assumes that
* the jstl taglibs will be inside the same bundle.
*
* @return Bundle contains the jstl implementation class
*/
public Bundle findJstlBundle()
{
Class<?> jstlClass = null;
try
{
jstlClass = getClass().getClassLoader().loadClass(JSTL_BUNDLE_CLASS);
return FrameworkUtil.getBundle(jstlClass);
}
catch (ClassNotFoundException e)
{
//no jstl do nothing
}
return null;
}
}

View File

@ -12,12 +12,6 @@
<Set name="contexts">
<Ref refid="Contexts" />
</Set>
<!--
<Call name="setContextAttribute">
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
<Arg>.*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$</Arg>
</Call>
-->
</New>
</Arg>
</Call>

View File

@ -37,18 +37,7 @@
</Set>
</New>
</Set>
<!--
<New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
<Set name="secureScheme">https</Set>
<Set name="securePort"><Property name="jetty.httpConfig.securePort" default="8443" /></Set>
<Set name="outputBufferSize">32768</Set>
<Set name="requestHeaderSize">8192</Set>
<Set name="responseHeaderSize">8192</Set>
<Set name="sendServerVersion">true</Set>
<Set name="sendDateHeader">false</Set>
<Set name="headerCacheSize">1024</Set>
</New>
-->
<New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
<Set name="secureScheme"><Property name="jetty.httpConfig.secureScheme" default="https" /></Set>
<Set name="securePort"><Property name="jetty.httpConfig.securePort" default="8443" /></Set>
@ -75,33 +64,6 @@
<Set name="dumpBeforeStop"><Property name="jetty.server.dumpBeforeStop" default="false"/></Set>
<Call class="org.eclipse.jetty.ee10.webapp.Configurations" name="setServerDefault">
<Arg><Ref refid="Server"/></Arg>
<Call name="add">
<Arg name="configClass">
<Array type="String">
<Item>org.eclipse.jetty.ee10.webapp.FragmentConfiguration</Item>
<Item>org.eclipse.jetty.ee10.webapp.JettyWebXmlConfiguration</Item>
<Item>org.eclipse.jetty.ee10.webapp.WebXmlConfiguration</Item>
<Item>org.eclipse.jetty.ee10.webapp.WebAppConfiguration</Item>
<Item>org.eclipse.jetty.ee10.webapp.ServletsConfiguration</Item>
<Item>org.eclipse.jetty.ee10.webapp.JspConfiguration</Item>
<Item>org.eclipse.jetty.ee10.webapp.JaasConfiguration</Item>
<Item>org.eclipse.jetty.ee10.webapp.JndiConfiguration</Item>
<Item>org.eclipse.jetty.plus.webapp.PlusConfiguration</Item>
<Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item>
<Item>org.eclipse.jetty.ee10.webapp.JmxConfiguration</Item>
<Item>org.eclipse.jetty.ee10.osgi.annotations.AnnotationConfiguration</Item>
<Item>org.eclipse.jetty.websocket.server.config.JettyWebSocketConfiguration</Item>
<Item>org.eclipse.jetty.websocket.jakarta.server.config.JakartaWebSocketConfiguration</Item>
<Item>org.eclipse.jetty.ee10.osgi.boot.OSGiWebInfConfiguration</Item>
<Item>org.eclipse.jetty.ee10.osgi.boot.OSGiMetaInfConfiguration</Item>
</Array>
</Arg>
</Call>
</Call>
<Call class="java.lang.System" name="setProperty">
<Arg>java.naming.factory.initial</Arg>
<Arg><Property name="java.naming.factory.initial" default="org.eclipse.jetty.jndi.InitialContextFactory"/></Arg>

View File

@ -25,7 +25,7 @@
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-deploy</artifactId>
<artifactId>jetty-osgi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
@ -41,9 +41,26 @@
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.cm</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.event</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.util.tracker</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
@ -73,8 +90,8 @@
<configuration>
<instructions>
<Bundle-SymbolicName>org.eclipse.jetty.ee10.osgi.boot;singleton:=true</Bundle-SymbolicName>
<Bundle-Activator>org.eclipse.jetty.ee10.osgi.boot.JettyBootstrapActivator</Bundle-Activator>
<DynamicImport-Package>org.eclipse.jetty.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))"</DynamicImport-Package>
<Bundle-Activator>org.eclipse.jetty.ee10.osgi.boot.EE10Activator</Bundle-Activator>
<DynamicImport-Package>org.eclipse.jetty.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))", org.eclipse.jetty.ee10.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))"</DynamicImport-Package>
<Import-Package>
${osgi.slf4j.import.packages},
jakarta.mail;version="2.0";resolution:=optional,
@ -82,21 +99,22 @@
jakarta.mail.internet;version="2.0";resolution:=optional,
jakarta.mail.search;version="2.0";resolution:=optional,
jakarta.mail.util;version="2.0";resolution:=optional,
jakarta.servlet;version="[$(version;==;${servletImpl.osgiVersion}),$(version;+;${servletImpl.osgiVersion}))",
jakarta.servlet.http;version="[$(version;==;${servletImpl.osgiVersion}),$(version;+;${servletImpl.osgiVersion}))",
jakarta.servlet;version="[$(version;==;${jakarta.servlet.api.version}),$(version;+;${jakarta.servlet.api.version}))",
jakarta.servlet.http;version="[$(version;==;${jakarta.servlet.api.version}),$(version;+;${jakarta.servlet.api.version}))",
jakarta.transaction;version="2.0.0";resolution:=optional,
jakarta.transaction.xa;version="2.0.0";resolution:=optional,
org.objectweb.asm;version="$(version;=;${asm.version})";resolution:=optional,
org.osgi.framework,
org.osgi.service.cm;version="1.4.0",
org.osgi.service.event;version="1.4.0",
org.osgi.service.cm;version="${osgi-service-cm-version}",
org.osgi.service.component;version="${osgi-service-component-version}",
org.osgi.service.event;version="${osgi-service-event-version}",
org.osgi.service.packageadmin,
org.osgi.service.startlevel;version="1.0.0",
org.osgi.service.url;version="1.0.0",
org.osgi.util.tracker;version="1.3.0",
org.osgi.util.tracker;version="${osgi-util-tracker-version}",
org.xml.sax,
org.xml.sax.helpers,
org.eclipse.jetty.annotations;resolution:=optional,
org.eclipse.jetty.ee10.annotations;resolution:=optional,
*
</Import-Package>
<Require-Capability>

View File

@ -13,19 +13,21 @@
package org.eclipse.jetty.ee10.osgi.annotations;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.HashSet;
import java.util.Set;
import jakarta.servlet.ServletContainerInitializer;
import org.eclipse.jetty.ee10.annotations.AnnotationConfiguration.ParserTask;
import org.eclipse.jetty.ee10.annotations.AnnotationConfiguration.TimeStatistic;
import org.eclipse.jetty.ee10.annotations.AnnotationParser.Handler;
import org.eclipse.jetty.ee10.osgi.boot.OSGiMetaInfConfiguration;
import org.eclipse.jetty.ee10.osgi.boot.OSGiWebappConstants;
import org.eclipse.jetty.ee10.webapp.Configuration;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.osgi.OSGiWebappConstants;
import org.eclipse.jetty.util.FileID;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
import org.eclipse.jetty.util.statistic.CounterStatistic;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
@ -80,18 +82,18 @@ public class AnnotationConfiguration extends org.eclipse.jetty.ee10.annotations.
* This parser scans the bundles using the OSGi APIs instead of assuming a jar.
*/
@Override
protected org.eclipse.jetty.ee10.annotations.AnnotationParser createAnnotationParser(int javaTargetVersion)
protected org.eclipse.jetty.ee10.annotations.AnnotationParser createAnnotationParser(int platform)
{
return new AnnotationParser(javaTargetVersion);
return new AnnotationParser(platform);
}
@Override
public Resource getJarFor(ServletContainerInitializer service) throws MalformedURLException, IOException
public Resource getJarFor(ServletContainerInitializer service)
{
Resource resource = super.getJarFor(service);
// TODO This is not correct, but implemented like this to be bug for bug compatible
// with previous implementation that could only handle actual jars and not bundles.
if (resource != null && !FileID.isJavaArchive(resource.getFileName()))
if (resource != null && !FileID.isJavaArchive(resource.getURI()))
return null;
return resource;
}
@ -127,7 +129,7 @@ public class AnnotationConfiguration extends org.eclipse.jetty.ee10.annotations.
if (bundle.getState() == Bundle.UNINSTALLED)
continue;
Resource bundleRes = oparser.indexBundle(bundle);
Resource bundleRes = oparser.indexBundle(ResourceFactory.of(context), bundle);
if (!context.getMetaData().getWebInfResources(false).contains(bundleRes))
{
context.getMetaData().addWebInfResource(bundleRes);
@ -142,7 +144,7 @@ public class AnnotationConfiguration extends org.eclipse.jetty.ee10.annotations.
}
}
//scan ourselves
oparser.indexBundle(webbundle);
oparser.indexBundle(ResourceFactory.of(context), webbundle);
parseWebBundle(context, oparser, webbundle);
_webInfLibStats.increment();

View File

@ -14,50 +14,54 @@
package org.eclipse.jetty.ee10.osgi.annotations;
import java.io.File;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jetty.ee10.osgi.boot.utils.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.util.FileID;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
*/
public class AnnotationParser extends org.eclipse.jetty.ee10.annotations.AnnotationParser
{
private Set<URI> _alreadyParsed = ConcurrentHashMap.newKeySet();
private static final Logger LOG = LoggerFactory.getLogger(AnnotationParser.class);
private Set<URI> _parsed = ConcurrentHashMap.newKeySet();
private ConcurrentHashMap<URI, Bundle> _uriToBundle = new ConcurrentHashMap<>();
private ConcurrentHashMap<Bundle, Resource> _bundleToResource = new ConcurrentHashMap<>();
private ConcurrentHashMap<Resource, Bundle> _resourceToBundle = new ConcurrentHashMap<>();
private ConcurrentHashMap<Bundle, URI> _bundleToUri = new ConcurrentHashMap<>();
public AnnotationParser(int javaPlatform)
public AnnotationParser()
{
super(javaPlatform);
super();
}
public AnnotationParser(int platform)
{
super(platform);
}
/**
* Keep track of a jetty URI Resource and its associated OSGi bundle.
*
*@param resourceFactory the ResourceFactory to convert bundle location
* @param bundle the bundle to index
* @return the resource for the bundle
* @throws Exception if unable to create the resource reference
*/
public Resource indexBundle(Bundle bundle) throws Exception
public Resource indexBundle(ResourceFactory resourceFactory, Bundle bundle) throws Exception
{
File bundleFile = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle);
Resource resource = Resource.newResource(bundleFile.toURI());
Resource resource = resourceFactory.newResource(bundleFile.toURI());
URI uri = resource.getURI();
_uriToBundle.putIfAbsent(uri, bundle);
_bundleToUri.putIfAbsent(bundle, uri);
@ -81,145 +85,57 @@ public class AnnotationParser extends org.eclipse.jetty.ee10.annotations.Annotat
return _resourceToBundle.get(resource);
}
/**
*
*/
@Override
public void parse(Set<? extends Handler> handlers, URI[] uris)
throws Exception
{
for (URI uri : uris)
{
Bundle associatedBundle = _uriToBundle.get(uri);
if (associatedBundle == null)
{
if (!_alreadyParsed.add(uri))
{
continue;
}
//a jar in WEB-INF/lib or the WEB-INF/classes
//use the behavior of the super class for a standard jar.
super.parse(handlers, new URI[]{uri});
}
else
{
parse(handlers, associatedBundle);
}
}
}
public void parse(Set<? extends Handler> handlers, Bundle bundle)
throws Exception
{
URI uri = _bundleToUri.get(bundle);
if (!_alreadyParsed.add(uri))
{
Resource bundleResource = _bundleToResource.get(bundle);
if (bundleResource == null)
return;
//if already added, it is already parsed
if (!_parsed.add(_bundleToUri.get(bundle)))
return;
parse(handlers, bundleResource);
}
String bundleClasspath = (String)bundle.getHeaders().get(Constants.BUNDLE_CLASSPATH);
if (bundleClasspath == null)
{
bundleClasspath = ".";
}
//order the paths first by the number of tokens in the path second alphabetically.
TreeSet<String> paths = new TreeSet<>(
new Comparator<String>()
{
@Override
public int compare(String o1, String o2)
public void parse(final Set<? extends Handler> handlers, Resource r) throws Exception
{
int paths1 = new StringTokenizer(o1, "/", false).countTokens();
int paths2 = new StringTokenizer(o2, "/", false).countTokens();
if (paths1 == paths2)
{
return o1.compareTo(o2);
}
return paths2 - paths1;
}
});
boolean hasDotPath = false;
StringTokenizer tokenizer = new StringTokenizer(bundleClasspath, StringUtil.DEFAULT_DELIMS, false);
while (tokenizer.hasMoreTokens())
{
String token = tokenizer.nextToken().trim();
if (!token.startsWith("/"))
{
token = "/" + token;
}
if (token.equals("/."))
{
hasDotPath = true;
}
else if (!FileID.isJavaArchive(token) && !token.endsWith("/"))
{
paths.add(token + "/");
}
else
{
paths.add(token);
}
}
//support the development environment: maybe the classes are inside bin or target/classes
//this is certainly not useful in production.
//however it makes our life so much easier during development.
if (bundle.getEntry("/.classpath") != null)
{
if (bundle.getEntry("/bin/") != null)
{
paths.add("/bin/");
}
else if (bundle.getEntry("/target/classes/") != null)
{
paths.add("/target/classes/");
}
}
@SuppressWarnings("rawtypes")
Enumeration classes = bundle.findEntries("/", "*.class", true);
if (classes == null)
if (r == null)
return;
if (!r.exists())
return;
if (FileID.isJavaArchive(r.getPath()))
{
parseJar(handlers, r);
return;
}
while (classes.hasMoreElements())
if (r.isDirectory())
{
URL classUrl = (URL)classes.nextElement();
String path = classUrl.getPath();
//remove the longest path possible:
String name = null;
for (String prefixPath : paths)
{
if (path.startsWith(prefixPath))
{
name = path.substring(prefixPath.length());
break;
}
}
if (name == null && hasDotPath)
{
//remove the starting '/'
name = path.substring(1);
}
if (name == null)
{
//found some .class file in the archive that was not under one of the prefix paths
//or the bundle classpath wasn't simply ".", so skip it
continue;
parseDir(handlers, r);
return;
}
if (!isValidClassFileName(name))
if (FileID.isClassFile(r.getPath()))
{
continue; //eg skip module-info.class
parseClass(handlers, null, r.getPath());
}
//transform into a classname to pass to the resolver
String shortName = StringUtil.replace(name, '/', '.').substring(0, name.length() - 6);
addParsedClass(shortName, getResource(bundle));
try (InputStream classInputStream = classUrl.openStream())
//Not already parsed, it could be a file that actually is compressed but does not have
//.jar/.zip etc extension, such as equinox urls, so try to parse it
try
{
scanClass(handlers, getResource(bundle), classInputStream);
parseJar(handlers, r);
}
catch (Exception e)
{
if (LOG.isDebugEnabled())
LOG.warn("Resource not able to be scanned for classes: {}", r);
}
}
}

View File

@ -1,235 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot;
import java.io.File;
import java.util.Dictionary;
import java.util.HashMap;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.ee10.osgi.boot.utils.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.ee10.osgi.boot.utils.OSGiClassLoader;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.resource.JarResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.osgi.framework.Bundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* AbstractContextProvider
*
* Base class for DeploymentManager Providers that can deploy ContextHandlers into
* Jetty that have been discovered via OSGI either as bundles or services.
*/
public abstract class AbstractContextProvider extends AbstractLifeCycle implements AppProvider
{
private static final Logger LOG = LoggerFactory.getLogger(AbstractContextProvider.class);
private DeploymentManager _deploymentManager;
private ServerInstanceWrapper _serverWrapper;
/**
* OSGiApp
*/
public class OSGiApp extends AbstractOSGiApp
{
private String _contextFile;
private ContextHandler _contextHandler;
private boolean _configured = false;
public OSGiApp(DeploymentManager manager, AppProvider provider, String originId, Bundle bundle, String contextFile)
{
super(manager, provider, bundle, originId);
_contextFile = contextFile;
}
public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String contextFile, String originId)
{
super(manager, provider, bundle, properties, originId);
_contextFile = contextFile;
}
public String getContextFile()
{
return _contextFile;
}
public void setHandler(ContextHandler h)
{
_contextHandler = h;
}
public ContextHandler createContextHandler()
throws Exception
{
configureContextHandler();
return _contextHandler;
}
public void configureContextHandler()
throws Exception
{
if (_configured)
return;
_configured = true;
//Override for bundle root may have been set
String bundleOverrideLocation = (String)_properties.get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE);
//Location on filesystem of bundle or the bundle override location
File bundleLocation = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(_bundle);
File root = (bundleOverrideLocation == null ? bundleLocation : new File(bundleOverrideLocation));
Resource rootResource = Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(root.toURI().toURL()));
//try and make sure the rootResource is useable - if its a jar then make it a jar file url
if (rootResource.exists() && !rootResource.isDirectory() && !rootResource.toString().startsWith("jar:"))
{
Resource jarResource = JarResource.newJarResource(rootResource);
if (jarResource.exists() && jarResource.isDirectory())
rootResource = jarResource;
}
//Set the base resource of the ContextHandler, if not already set, can also be overridden by the context xml file
if (_contextHandler != null && _contextHandler.getResourceBase() == null)
{
_contextHandler.setResourceBase(rootResource.getPath());
}
//Use a classloader that knows about the common jetty parent loader, and also the bundle
OSGiClassLoader classLoader = new OSGiClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps(), _bundle);
//if there is a context file, find it and apply it
if (_contextFile == null && _contextHandler == null)
throw new IllegalStateException("No context file or ContextHandler");
if (_contextFile != null)
{
//apply the contextFile, creating the ContextHandler, the DeploymentManager will register it in the ContextHandlerCollection
Resource res = null;
String jettyHome = (String)getServerInstanceWrapper().getServer().getAttribute(OSGiServerConstants.JETTY_HOME);
if (jettyHome == null)
jettyHome = System.getProperty(OSGiServerConstants.JETTY_HOME);
res = findFile(_contextFile, jettyHome, bundleOverrideLocation, _bundle);
//apply the context xml file, either to an existing ContextHandler, or letting the
//it create the ContextHandler as necessary
if (res != null)
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (LOG.isDebugEnabled())
LOG.debug("Context classloader = {}", cl);
try
{
Thread.currentThread().setContextClassLoader(classLoader);
XmlConfiguration xmlConfiguration = new XmlConfiguration(res);
HashMap properties = new HashMap();
//put the server instance in
properties.put("Server", getServerInstanceWrapper().getServer());
//put in the location of the bundle root
properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, rootResource.toString());
// insert the bundle's location as a property.
xmlConfiguration.getProperties().putAll(properties);
if (_contextHandler == null)
_contextHandler = (ContextHandler)xmlConfiguration.configure();
else
xmlConfiguration.configure(_contextHandler);
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
}
//Set up the class loader we created
_contextHandler.setClassLoader(classLoader);
//If a bundle/service property specifies context path, let it override the context xml
String contextPath = (String)_properties.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
if (contextPath != null)
_contextHandler.setContextPath(contextPath);
//osgi Enterprise Spec r4 p.427
_contextHandler.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, _bundle.getBundleContext());
//make sure we protect also the osgi dirs specified by OSGi Enterprise spec
if (_contextHandler instanceof ServletContextHandler servletContextHandler)
{
String[] targets = servletContextHandler.getProtectedTargets();
int length = (targets == null ? 0 : targets.length);
String[] updatedTargets = null;
if (targets != null)
{
updatedTargets = new String[length + OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
System.arraycopy(targets, 0, updatedTargets, 0, length);
}
else
updatedTargets = new String[OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length);
servletContextHandler.setProtectedTargets(updatedTargets);
}
}
}
public AbstractContextProvider(ServerInstanceWrapper wrapper)
{
_serverWrapper = wrapper;
}
public ServerInstanceWrapper getServerInstanceWrapper()
{
return _serverWrapper;
}
@Override
public ContextHandler createContextHandler(App app) throws Exception
{
if (app == null)
return null;
if (!(app instanceof OSGiApp))
throw new IllegalStateException(app + " is not a BundleApp");
//Create a ContextHandler suitable to deploy in OSGi
ContextHandler h = ((OSGiApp)app).createContextHandler();
return h;
}
@Override
public void setDeploymentManager(DeploymentManager deploymentManager)
{
_deploymentManager = deploymentManager;
}
public DeploymentManager getDeploymentManager()
{
return _deploymentManager;
}
}

View File

@ -1,195 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot;
import java.io.File;
import java.net.URL;
import java.util.Dictionary;
import java.util.Hashtable;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.resource.Resource;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* AbstractOSGiApp
*
* Base class representing info about a webapp/ContextHandler that is deployed into Jetty.
*/
public abstract class AbstractOSGiApp extends App
{
private static final Logger LOG = LoggerFactory.getLogger(AbstractOSGiApp.class);
protected Bundle _bundle;
protected Dictionary<?, ?> _properties;
protected ServiceRegistration _registration;
public AbstractOSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId)
{
this(manager, provider, bundle, bundle.getHeaders(), originId);
}
public AbstractOSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary<?, ?> properties, String originId)
{
super(manager, provider, null, originId); // TODO environment
_properties = properties;
_bundle = bundle;
}
public String getBundleSymbolicName()
{
return _bundle.getSymbolicName();
}
public String getBundleVersionAsString()
{
if (_bundle.getVersion() == null)
return null;
return _bundle.getVersion().toString();
}
public Bundle getBundle()
{
return _bundle;
}
public void setRegistration(ServiceRegistration registration)
{
_registration = registration;
}
public ServiceRegistration getRegistration()
{
return _registration;
}
public void registerAsOSGiService() throws Exception
{
if (_registration == null)
{
Dictionary<String, String> properties = new Hashtable<String, String>();
properties.put(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK);
if (getBundleSymbolicName() != null)
properties.put(OSGiWebappConstants.OSGI_WEB_SYMBOLICNAME, getBundleSymbolicName());
if (getBundleVersionAsString() != null)
properties.put(OSGiWebappConstants.OSGI_WEB_VERSION, getBundleVersionAsString());
properties.put(OSGiWebappConstants.OSGI_WEB_CONTEXTPATH, getContextPath());
ServiceRegistration rego = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(ContextHandler.class.getName(), getContextHandler(), properties);
setRegistration(rego);
}
}
protected void deregisterAsOSGiService() throws Exception
{
if (_registration == null)
return;
_registration.unregister();
_registration = null;
}
protected Resource getFileAsResource(String dir, String file)
{
Resource r = null;
try
{
File asFile = new File(dir, file);
if (asFile.exists())
r = Resource.newResource(asFile);
}
catch (Exception e)
{
r = null;
}
return r;
}
protected Resource getFileAsResource(String file)
{
Resource r = null;
try
{
File asFile = new File(file);
if (asFile.exists())
r = Resource.newResource(asFile);
}
catch (Exception e)
{
r = null;
}
return r;
}
protected Resource findFile(String fileName, String jettyHome, String bundleOverrideLocation, Bundle containingBundle)
{
Resource res = null;
//try to find the context file in the filesystem
if (fileName.startsWith("/"))
res = getFileAsResource(fileName);
if (res != null)
return res;
//try to find it relative to jetty home
if (jettyHome != null)
{
if (jettyHome.startsWith("\"") || jettyHome.startsWith("'"))
jettyHome = jettyHome.substring(1);
if (jettyHome.endsWith("\"") || (jettyHome.endsWith("'")))
jettyHome = jettyHome.substring(0, jettyHome.length() - 1);
res = getFileAsResource(jettyHome, fileName);
}
if (res != null)
return res;
//try to find it relative to an override location that has been specified
if (bundleOverrideLocation != null)
{
try (Resource location = Resource.newResource(bundleOverrideLocation))
{
res = location.addPath(fileName);
}
catch (Exception e)
{
LOG.warn("Unable to find relative override location: {}", bundleOverrideLocation, e);
}
}
if (res != null)
return res;
//try to find it relative to the bundle in which it is being deployed
if (containingBundle != null)
{
if (fileName.startsWith("./"))
fileName = fileName.substring(1);
if (!fileName.startsWith("/"))
fileName = "/" + fileName;
URL entry = _bundle.getEntry(fileName);
if (entry != null)
res = Resource.newResource(entry);
}
return res;
}
}

View File

@ -1,553 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot;
import java.io.File;
import java.net.URI;
import java.net.URL;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.ee10.osgi.boot.internal.webapp.OSGiWebappClassLoader;
import org.eclipse.jetty.ee10.osgi.boot.utils.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.ee10.webapp.WebAppClassLoader;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.resource.JarResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.packageadmin.PackageAdmin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* AbstractWebAppProvider
* <p>
* Base class for Jetty DeploymentManager Providers that are capable of deploying a webapp,
* either from a bundle or an OSGi service.
*/
public abstract class AbstractWebAppProvider extends AbstractLifeCycle implements AppProvider
{
private static final Logger LOG = LoggerFactory.getLogger(AbstractWebAppProvider.class);
private boolean _parentLoaderPriority;
private String _defaultsDescriptor;
private boolean _extractWars = true; //See WebAppContext.extractWars
private String _tldBundles;
private DeploymentManager _deploymentManager;
private ServerInstanceWrapper _serverWrapper;
/**
* OSGiApp
*
* Represents a deployable webapp.
*/
public class OSGiApp extends AbstractOSGiApp
{
private String _contextPath;
private String _webAppPath;
private WebAppContext _webApp;
public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId)
{
super(manager, provider, bundle, originId);
}
public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId)
{
super(manager, provider, bundle, properties, originId);
}
public void setWebAppContext(WebAppContext webApp)
{
_webApp = webApp;
}
@Override
public String getContextPath()
{
return _contextPath;
}
public void setContextPath(String contextPath)
{
this._contextPath = contextPath;
}
public String getBundlePath()
{
return _webAppPath;
}
public void setWebAppPath(String path)
{
this._webAppPath = path;
}
public ContextHandler createContextHandler()
throws Exception
{
if (_webApp != null)
{
configureWebApp();
return _webApp;
}
createWebApp();
return _webApp;
}
protected void createWebApp()
throws Exception
{
_webApp = newWebApp();
configureWebApp();
}
protected WebAppContext newWebApp()
{
WebAppContext webApp = new WebAppContext();
webApp.setAttribute(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK);
//make sure we protect also the osgi dirs specified by OSGi Enterprise spec
String[] targets = webApp.getProtectedTargets();
String[] updatedTargets = null;
if (targets != null)
{
updatedTargets = new String[targets.length + OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
System.arraycopy(targets, 0, updatedTargets, 0, targets.length);
}
else
updatedTargets = new String[OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, targets.length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length);
webApp.setProtectedTargets(updatedTargets);
return webApp;
}
public void configureWebApp()
throws Exception
{
//TODO turn this around and let any context.xml file get applied first, and have the properties override
_webApp.setContextPath(_contextPath);
//osgi Enterprise Spec r4 p.427
_webApp.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, _bundle.getBundleContext());
String overrideBundleInstallLocation = (String)_properties.get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE);
File bundleInstallLocation =
(overrideBundleInstallLocation == null
? BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(_bundle)
: new File(overrideBundleInstallLocation));
if (LOG.isDebugEnabled())
{
LOG.debug("Bundle location is {}, install location: {}", _bundle.getLocation(), bundleInstallLocation);
}
URL url = null;
Resource rootResource = Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(bundleInstallLocation.toURI().toURL()));
//try and make sure the rootResource is useable - if its a jar then make it a jar file url
if (rootResource.exists() && !rootResource.isDirectory() && !rootResource.toString().startsWith("jar:"))
{
Resource jarResource = JarResource.newJarResource(rootResource);
if (jarResource.exists() && jarResource.isDirectory())
rootResource = jarResource;
}
//if the path wasn't set or it was ., then it is the root of the bundle's installed location
if (_webAppPath == null || _webAppPath.length() == 0 || ".".equals(_webAppPath))
{
url = bundleInstallLocation.toURI().toURL();
if (LOG.isDebugEnabled())
LOG.debug("Webapp base using bundle install location: {}", url);
}
else
{
//Get the location of the root of the webapp inside the installed bundle
if (_webAppPath.startsWith("/") || _webAppPath.startsWith("file:"))
{
url = new File(_webAppPath).toURI().toURL();
if (LOG.isDebugEnabled())
LOG.debug("Webapp base using absolute location: {}", url);
}
else if (bundleInstallLocation != null && bundleInstallLocation.isDirectory())
{
url = new File(bundleInstallLocation, _webAppPath).toURI().toURL();
if (LOG.isDebugEnabled())
LOG.debug("Webapp base using path relative to bundle unpacked install location: {}", url);
}
else if (bundleInstallLocation != null)
{
Enumeration<URL> urls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(_bundle, _webAppPath);
if (urls != null && urls.hasMoreElements())
{
url = urls.nextElement();
if (LOG.isDebugEnabled())
LOG.debug("Webapp base using path relative to packed bundle location: {}", url);
}
}
}
if (url == null)
{
throw new IllegalArgumentException("Unable to locate " + _webAppPath + " in " +
(bundleInstallLocation != null ? bundleInstallLocation.getAbsolutePath() : "unlocated bundle '" + _bundle.getSymbolicName() + "'"));
}
//Sets the location of the war file
// converts bundleentry: protocol if necessary
_webApp.setWar(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(url).toString());
// Set up what has been configured on the provider
_webApp.setParentLoaderPriority(isParentLoaderPriority());
_webApp.setExtractWAR(isExtract());
//Set up configuration from manifest headers
//extra classpath
String tmp = (String)_properties.get(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH);
if (tmp != null)
_webApp.setExtraClasspath(tmp);
//web.xml
tmp = (String)_properties.get(OSGiWebappConstants.JETTY_WEB_XML_PATH);
if (tmp != null && tmp.trim().length() != 0)
{
File webXml = getFile(tmp, bundleInstallLocation);
if (webXml != null && webXml.exists())
_webApp.setDescriptor(webXml.getAbsolutePath());
}
//webdefault-ee10.xml
tmp = (String)_properties.get(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH);
if (tmp != null)
{
File defaultWebXml = getFile(tmp, bundleInstallLocation);
if (defaultWebXml != null)
{
if (defaultWebXml.exists())
_webApp.setDefaultsDescriptor(defaultWebXml.getAbsolutePath());
else
LOG.warn("{} does not exist", defaultWebXml.getAbsolutePath());
}
}
//Handle Require-TldBundle
//This is a comma separated list of names of bundles that contain tlds that this webapp uses.
//We add them to the webapp classloader.
String requireTldBundles = (String)_properties.get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
String pathsToTldBundles = getPathsToRequiredBundles(requireTldBundles);
// make sure we provide access to all the jetty bundles by going
// through this bundle.
OSGiWebappClassLoader webAppLoader = new OSGiWebappClassLoader(_serverWrapper.getParentClassLoaderForWebapps(), _webApp, _bundle);
if (pathsToTldBundles != null)
webAppLoader.addClassPath(pathsToTldBundles);
_webApp.setClassLoader(webAppLoader);
// apply any META-INF/context.xml file that is found to configure
// the webapp first
try
{
final Resource finalRootResource = rootResource;
WebAppClassLoader.runWithServerClassAccess(() ->
{
applyMetaInfContextXml(finalRootResource, overrideBundleInstallLocation);
return null;
});
}
catch (Exception e)
{
LOG.warn("Error applying context xml");
throw e;
}
_webApp.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundles);
//Set up some attributes
// rfc66
_webApp.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT, _bundle.getBundleContext());
// spring-dm-1.2.1 looks for the BundleContext as a different attribute.
// not a spec... but if we want to support
// org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext
// then we need to do this to:
_webApp.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(), _bundle.getBundleContext());
// also pass the bundle directly. sometimes a bundle does not have a
// bundlecontext.
// it is still useful to have access to the Bundle from the servlet
// context.
_webApp.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, _bundle);
}
protected String getPathsToRequiredBundles(String requireTldBundles)
throws Exception
{
if (requireTldBundles == null)
return null;
ServiceReference ref = _bundle.getBundleContext().getServiceReference(org.osgi.service.packageadmin.PackageAdmin.class.getName());
PackageAdmin packageAdmin = (ref == null) ? null : (PackageAdmin)_bundle.getBundleContext().getService(ref);
if (packageAdmin == null)
throw new IllegalStateException("Unable to get PackageAdmin reference to locate required Tld bundles");
StringBuilder paths = new StringBuilder();
String[] symbNames = requireTldBundles.split("[, ]");
for (String symbName : symbNames)
{
Bundle[] bs = packageAdmin.getBundles(symbName, null);
if (bs == null || bs.length == 0)
{
throw new IllegalArgumentException("Unable to locate the bundle '" + symbName + "' specified by " +
OSGiWebappConstants.REQUIRE_TLD_BUNDLE + " in manifest of " +
(_bundle == null ? "unknown" : _bundle.getSymbolicName()));
}
File f = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bs[0]);
if (paths.length() > 0)
paths.append(", ");
paths.append(f.toURI().toURL().toString());
if (LOG.isDebugEnabled())
LOG.debug("getPathsToRequiredBundles: bundle path={} uri={}", bs[0].getLocation(), f.toURI());
}
return paths.toString();
}
protected void applyMetaInfContextXml(Resource rootResource, String overrideBundleInstallLocation)
throws Exception
{
if (_bundle == null)
return;
if (_webApp == null)
return;
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (LOG.isDebugEnabled())
LOG.debug("Context classloader = {}", cl);
try
{
Thread.currentThread().setContextClassLoader(_webApp.getClassLoader());
URI contextXmlUri = null;
//TODO replace this with getting the InputStream so we don't cache in URL
//Try looking for a context xml file in META-INF with a specific name
URL url = _bundle.getEntry("/META-INF/jetty-webapp-context.xml");
if (url != null)
{
contextXmlUri = url.toURI();
}
if (contextXmlUri == null)
{
//Didn't find specially named file, try looking for a property that names a context xml file to use
if (_properties != null)
{
String tmp = (String)_properties.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
if (tmp != null)
{
String[] filenames = tmp.split("[,;]");
if (filenames != null && filenames.length > 0)
{
String filename = filenames[0]; //should only be 1 filename in this usage
String jettyHome = (String)getServerInstanceWrapper().getServer().getAttribute(OSGiServerConstants.JETTY_HOME);
if (jettyHome == null)
jettyHome = System.getProperty(OSGiServerConstants.JETTY_HOME);
Resource res = findFile(filename, jettyHome, overrideBundleInstallLocation, _bundle);
if (res != null)
{
contextXmlUri = res.getURI();
}
}
}
}
}
if (contextXmlUri == null)
return;
// Apply it just as the standard jetty ContextProvider would do
LOG.info("Applying {} to {}", contextXmlUri, _webApp);
XmlConfiguration xmlConfiguration = new XmlConfiguration(Resource.newResource(contextXmlUri));
WebAppClassLoader.runWithServerClassAccess(() ->
{
HashMap<String, String> properties = new HashMap<>();
xmlConfiguration.getIdMap().put("Server", getDeploymentManager().getServer());
properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, rootResource.toString());
properties.put(OSGiServerConstants.JETTY_HOME, (String)getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME));
xmlConfiguration.getProperties().putAll(properties);
xmlConfiguration.configure(_webApp);
return null;
});
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
private File getFile(String file, File bundleInstall)
{
if (file == null)
return null;
if (file.startsWith("/") || file.startsWith("file:/")) //absolute location
return new File(file);
else
{
//relative location
//try inside the bundle first
File f = new File(bundleInstall, file);
if (f.exists())
return f;
String jettyHome = (String)getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME);
if (jettyHome != null)
return new File(jettyHome, file);
}
return null;
}
}
public AbstractWebAppProvider(ServerInstanceWrapper wrapper)
{
_serverWrapper = wrapper;
}
/**
* Get the parentLoaderPriority.
*
* @return the parentLoaderPriority
*/
public boolean isParentLoaderPriority()
{
return _parentLoaderPriority;
}
/**
* Set the parentLoaderPriority.
*
* @param parentLoaderPriority the parentLoaderPriority to set
*/
public void setParentLoaderPriority(boolean parentLoaderPriority)
{
_parentLoaderPriority = parentLoaderPriority;
}
/**
* Get the defaultsDescriptor.
*
* @return the defaultsDescriptor
*/
public String getDefaultsDescriptor()
{
return _defaultsDescriptor;
}
/**
* Set the defaultsDescriptor.
*
* @param defaultsDescriptor the defaultsDescriptor to set
*/
public void setDefaultsDescriptor(String defaultsDescriptor)
{
_defaultsDescriptor = defaultsDescriptor;
}
public boolean isExtract()
{
return _extractWars;
}
public void setExtract(boolean extract)
{
_extractWars = extract;
}
/**
* @param tldBundles Comma separated list of bundles that contain tld jars
* that should be setup on the jetty instances created here.
*/
public void setTldBundles(String tldBundles)
{
_tldBundles = tldBundles;
}
/**
* @return The list of bundles that contain tld jars that should be setup on
* the jetty instances created here.
*/
public String getTldBundles()
{
return _tldBundles;
}
public void setServerInstanceWrapper(ServerInstanceWrapper wrapper)
{
_serverWrapper = wrapper;
}
public ServerInstanceWrapper getServerInstanceWrapper()
{
return _serverWrapper;
}
public DeploymentManager getDeploymentManager()
{
return _deploymentManager;
}
@Override
public void setDeploymentManager(DeploymentManager deploymentManager)
{
_deploymentManager = deploymentManager;
}
@Override
public ContextHandler createContextHandler(App app) throws Exception
{
if (app == null)
return null;
if (!(app instanceof OSGiApp))
throw new IllegalStateException(app + " is not a BundleApp");
//Create a WebAppContext suitable to deploy in OSGi
ContextHandler ch = ((OSGiApp)app).createContextHandler();
return ch;
}
public static String getOriginId(Bundle contributor, String path)
{
return contributor.getSymbolicName() + "-" + contributor.getVersion().toString() + (path.startsWith("/") ? path : "/" + path);
}
}

View File

@ -1,257 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.ee10.osgi.boot.utils.Util;
import org.eclipse.jetty.util.StringUtil;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.BundleTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* BundleWebAppProvider
* <p>
* A Jetty Provider that knows how to deploy a WebApp contained inside a Bundle.
*/
public class BundleWebAppProvider extends AbstractWebAppProvider implements BundleProvider
{
private static final Logger LOG = LoggerFactory.getLogger(AbstractWebAppProvider.class);
/**
* Map of Bundle to App. Used when a Bundle contains a webapp.
*/
private Map<Bundle, App> _bundleMap = new HashMap<>();
private ServiceRegistration _serviceRegForBundles;
private WebAppTracker _webappTracker;
public class WebAppTracker extends BundleTracker
{
protected String _managedServerName;
public WebAppTracker(BundleContext bundleContext, String managedServerName)
{
super(bundleContext, Bundle.ACTIVE | Bundle.STOPPING, null);
_managedServerName = managedServerName;
}
@Override
public Object addingBundle(Bundle bundle, BundleEvent event)
{
try
{
String serverName = (String)bundle.getHeaders().get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
if ((StringUtil.isBlank(serverName) && _managedServerName.equals(OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME)) ||
(!StringUtil.isBlank(serverName) && (serverName.equals(_managedServerName))))
{
if (bundleAdded(bundle))
return bundle;
}
}
catch (Exception e)
{
LOG.warn("Unable to add bundle {}", bundle, e);
}
return null;
}
@Override
public void removedBundle(Bundle bundle, BundleEvent event, Object object)
{
try
{
bundleRemoved(bundle);
}
catch (Exception e)
{
LOG.warn("Unable to remove bundle {}", bundle, e);
}
}
}
public BundleWebAppProvider(ServerInstanceWrapper wrapper)
{
super(wrapper);
}
@Override
protected void doStart() throws Exception
{
_webappTracker = new WebAppTracker(FrameworkUtil.getBundle(this.getClass()).getBundleContext(), getServerInstanceWrapper().getManagedServerName());
_webappTracker.open();
//register as an osgi service for deploying bundles, advertising the name of the jetty Server instance we are related to
Dictionary<String, String> properties = new Hashtable<>();
properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
_serviceRegForBundles = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(BundleProvider.class.getName(), this, properties);
super.doStart();
}
@Override
protected void doStop() throws Exception
{
_webappTracker.close();
//unregister ourselves
if (_serviceRegForBundles != null)
{
try
{
_serviceRegForBundles.unregister();
}
catch (Exception e)
{
LOG.warn("Unable to unregister {}", _serviceRegForBundles, e);
}
}
super.doStop();
}
/**
* A bundle has been added that could be a webapp
*
* @param bundle the bundle
*/
@Override
public boolean bundleAdded(Bundle bundle) throws Exception
{
if (bundle == null)
return false;
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps());
String contextPath = null;
try
{
@SuppressWarnings("unchecked")
Dictionary<String, String> headers = bundle.getHeaders();
//does the bundle have a OSGiWebappConstants.JETTY_WAR_FOLDER_PATH
String resourcePath = Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_RESOURCE_PATH, headers);
if (resourcePath != null)
{
String base = resourcePath;
contextPath = getContextPath(bundle);
String originId = getOriginId(bundle, base);
//TODO : we don't know whether an app is actually deployed, as deploymentManager swallows all
//exceptions inside the impl of addApp. Need to send the Event and also register as a service
//only if the deployment succeeded
OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
app.setWebAppPath(base);
app.setContextPath(contextPath);
_bundleMap.put(bundle, app);
getDeploymentManager().addApp(app);
return true;
}
//does the bundle have a WEB-INF/web.xml
if (bundle.getEntry("/WEB-INF/web.xml") != null)
{
String base = ".";
contextPath = getContextPath(bundle);
String originId = getOriginId(bundle, base);
OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
app.setContextPath(contextPath);
app.setWebAppPath(base);
_bundleMap.put(bundle, app);
getDeploymentManager().addApp(app);
return true;
}
//does the bundle define a OSGiWebappConstants.RFC66_WEB_CONTEXTPATH
if (headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH) != null)
{
//Could be a static webapp with no web.xml
String base = ".";
contextPath = headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
String originId = getOriginId(bundle, base);
OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId);
app.setContextPath(contextPath);
app.setWebAppPath(base);
_bundleMap.put(bundle, app);
getDeploymentManager().addApp(app);
return true;
}
return false;
}
catch (Exception e)
{
throw e;
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
/**
* Bundle has been removed. If it was a webapp we deployed, undeploy it.
*
* @param bundle the bundle
* @return true if this was a webapp we had deployed, false otherwise
*/
@Override
public boolean bundleRemoved(Bundle bundle) throws Exception
{
App app = _bundleMap.remove(bundle);
if (app != null)
{
getDeploymentManager().removeApp(app);
return true;
}
return false;
}
private static String getContextPath(Bundle bundle)
{
Dictionary<?, ?> headers = bundle.getHeaders();
String contextPath = (String)headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
if (contextPath == null)
{
// extract from the last token of the bundle's location:
// (really ?could consider processing the symbolic name as an alternative
// the location will often reflect the version.
// maybe this is relevant when the file is a war)
String location = bundle.getLocation();
String[] toks = StringUtil.replace(location, '\\', '/').split("/");
contextPath = toks[toks.length - 1];
// remove .jar, .war etc:
int lastDot = contextPath.lastIndexOf('.');
if (lastDot != -1)
contextPath = contextPath.substring(0, lastDot);
}
if (!contextPath.startsWith("/"))
contextPath = "/" + contextPath;
return contextPath;
}
}

View File

@ -0,0 +1,613 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot;
import java.io.File;
import java.net.URI;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.ee10.webapp.Configuration;
import org.eclipse.jetty.ee10.webapp.Configurations;
import org.eclipse.jetty.ee10.webapp.WebAppClassLoader;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.osgi.AbstractContextProvider;
import org.eclipse.jetty.osgi.BundleContextProvider;
import org.eclipse.jetty.osgi.BundleWebAppProvider;
import org.eclipse.jetty.osgi.ContextFactory;
import org.eclipse.jetty.osgi.OSGiApp;
import org.eclipse.jetty.osgi.OSGiServerConstants;
import org.eclipse.jetty.osgi.OSGiWebappConstants;
import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.util.FakeURLClassLoader;
import org.eclipse.jetty.osgi.util.OSGiClassLoader;
import org.eclipse.jetty.osgi.util.ServerClasspathContributor;
import org.eclipse.jetty.osgi.util.Util;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.FileID;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
import org.eclipse.jetty.util.resource.URLResourceFactory;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* EE10Activator
* <p>
* Enable deployment of webapps/contexts to E10E
*/
public class EE10Activator implements BundleActivator
{
private static final Logger LOG = LoggerFactory.getLogger(EE10Activator.class);
public static final String ENVIRONMENT = "ee10";
private static Collection<ServerClasspathContributor> __serverClasspathContributors = new ArrayList<>();
public static void registerServerClasspathContributor(ServerClasspathContributor contributor)
{
__serverClasspathContributors.add(contributor);
}
public static void unregisterServerClasspathContributor(ServerClasspathContributor contributor)
{
__serverClasspathContributors.remove(contributor);
}
public static Collection<ServerClasspathContributor> getServerClasspathContributors()
{
return __serverClasspathContributors;
}
/**
* ServerTracker
*
* Tracks appearance of Server instances as OSGi services, and then configures them
* for deployment of EE10 contexts and webapps.
*
*/
public static class ServerTracker implements ServiceTrackerCustomizer<Server, Object>
{
private Bundle _myBundle = null;
public ServerTracker(Bundle bundle)
{
_myBundle = bundle;
}
@Override
public Object addingService(ServiceReference<Server> sr)
{
Bundle contributor = sr.getBundle();
Server server = contributor.getBundleContext().getService(sr);
//find bundles that should be on the container classpath and convert to URLs
List<URL> contributedURLs = new ArrayList<>();
List<Bundle> contributedBundles = new ArrayList<>();
Collection<ServerClasspathContributor> serverClasspathContributors = getServerClasspathContributors();
serverClasspathContributors.stream().forEach(c -> contributedBundles.addAll(c.getScannableBundles()));
contributedBundles.stream().forEach(b -> contributedURLs.addAll(convertBundleToURL(b)));
if (!contributedURLs.isEmpty())
{
//There should already be a default set up by the JettyServerFactory
ClassLoader serverClassLoader = (ClassLoader)server.getAttribute(OSGiServerConstants.SERVER_CLASSLOADER);
if (serverClassLoader != null)
{
server.setAttribute(OSGiServerConstants.SERVER_CLASSLOADER,
new FakeURLClassLoader(serverClassLoader, contributedURLs.toArray(new URL[contributedURLs.size()])));
if (LOG.isDebugEnabled())
LOG.debug("Server classloader for contexts = {}", server.getAttribute(OSGiServerConstants.SERVER_CLASSLOADER));
}
server.setAttribute(OSGiServerConstants.SERVER_CLASSPATH_BUNDLES, contributedBundles);
}
Optional<DeploymentManager> deployer = getDeploymentManager(server);
BundleWebAppProvider webAppProvider = null;
BundleContextProvider contextProvider = null;
String containerScanBundlePattern = null;
if (contributedBundles != null)
{
StringBuffer strbuff = new StringBuffer();
contributedBundles.stream().forEach(b -> strbuff.append(b.getSymbolicName()).append("|"));
if (strbuff.length() > 0)
containerScanBundlePattern = strbuff.toString().substring(0, strbuff.length() - 1);
}
if (deployer.isPresent())
{
for (AppProvider provider : deployer.get().getAppProviders())
{
if (BundleContextProvider.class.isInstance(provider) && ENVIRONMENT.equalsIgnoreCase(provider.getEnvironmentName()))
contextProvider = BundleContextProvider.class.cast(provider);
if (BundleWebAppProvider.class.isInstance(provider) && ENVIRONMENT.equalsIgnoreCase(provider.getEnvironmentName()))
webAppProvider = BundleWebAppProvider.class.cast(provider);
}
if (contextProvider == null)
{
contextProvider = new BundleContextProvider(ENVIRONMENT, server, new EE10ContextFactory(_myBundle));
deployer.get().addAppProvider(contextProvider);
}
if (webAppProvider == null)
{
webAppProvider = new BundleWebAppProvider(ENVIRONMENT, server, new EE10WebAppFactory(_myBundle));
deployer.get().addAppProvider(webAppProvider);
}
//ensure the providers are configured with the extra bundles that must be scanned from the container classpath
if (containerScanBundlePattern != null)
{
contextProvider.getProperties().put(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, containerScanBundlePattern);
webAppProvider.getProperties().put(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, containerScanBundlePattern);
}
}
else
LOG.info("No DeploymentManager for Server {}", server);
try
{
if (!server.isStarted())
server.start();
}
catch (Exception e)
{
LOG.warn("Failed to start server {}", server);
}
return server;
}
@Override
public void modifiedService(ServiceReference<Server> reference, Object service)
{
removedService(reference, service);
addingService(reference);
}
@Override
public void removedService(ServiceReference<Server> reference, Object service)
{
}
private Optional<DeploymentManager> getDeploymentManager(Server server)
{
Collection<DeploymentManager> deployers = server.getBeans(DeploymentManager.class);
return deployers.stream().findFirst();
}
private List<URL> convertBundleToURL(Bundle bundle)
{
List<URL> urls = new ArrayList<>();
try
{
File file = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle);
if (file.isDirectory())
{
for (File f : file.listFiles())
{
if (FileID.isJavaArchive(f.getName()) && f.isFile())
{
urls.add(f.toURI().toURL());
}
else if (f.isDirectory() && f.getName().equals("lib"))
{
for (File f2 : file.listFiles())
{
if (FileID.isJavaArchive(f2.getName()) && f2.isFile())
{
urls.add(f2.toURI().toURL());
}
}
}
}
urls.add(file.toURI().toURL());
}
else
{
urls.add(file.toURI().toURL());
}
}
catch (Exception e)
{
LOG.warn("Unable to convert bundle {} to url", bundle, e);
}
return urls;
}
}
public static class EE10ContextFactory implements ContextFactory
{
private Bundle _myBundle;
public EE10ContextFactory(Bundle bundle)
{
_myBundle = bundle;
}
@Override
public ContextHandler createContextHandler(AbstractContextProvider provider, App app)
throws Exception
{
OSGiApp osgiApp = OSGiApp.class.cast(app);
String jettyHome = (String)app.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME);
Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome));
ContextHandler contextHandler = new ContextHandler();
//Make base resource that of the bundle
contextHandler.setBaseResource(osgiApp.getBundleResource());
// provides access to core classes
ClassLoader coreLoader = (ClassLoader)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER);
if (LOG.isDebugEnabled())
LOG.debug("Core classloader = {}", coreLoader.getClass());
//provide access to all ee10 classes
ClassLoader environmentLoader = new OSGiClassLoader(coreLoader, _myBundle);
//Use a classloader that knows about the common jetty parent loader, and also the bundle
OSGiClassLoader classLoader = new OSGiClassLoader(environmentLoader, osgiApp.getBundle());
contextHandler.setClassLoader(classLoader);
//Apply any context xml file
String tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
final URI contextXmlURI = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath);
if (contextXmlURI != null)
{
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
try
{
Thread.currentThread().setContextClassLoader(contextHandler.getClassLoader());
WebAppClassLoader.runWithServerClassAccess(() ->
{
XmlConfiguration xmlConfiguration = new XmlConfiguration(ResourceFactory.of(contextHandler).newResource(contextXmlURI));
WebAppClassLoader.runWithServerClassAccess(() ->
{
Map<String, String> properties = new HashMap<>();
xmlConfiguration.getIdMap().put("Server", osgiApp.getDeploymentManager().getServer());
properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString());
properties.put(OSGiServerConstants.JETTY_HOME, (String)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME));
xmlConfiguration.getProperties().putAll(properties);
xmlConfiguration.configure(contextHandler);
return null;
});
return null;
});
}
catch (Exception e)
{
LOG.warn("Error applying context xml", e);
throw e;
}
finally
{
Thread.currentThread().setContextClassLoader(oldLoader);
}
}
//osgi Enterprise Spec r4 p.427
contextHandler.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, osgiApp.getBundle().getBundleContext());
//make sure we protect also the osgi dirs specified by OSGi Enterprise spec
String[] targets = contextHandler.getProtectedTargets();
int length = (targets == null ? 0 : targets.length);
String[] updatedTargets = null;
if (targets != null)
{
updatedTargets = new String[length + OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
System.arraycopy(targets, 0, updatedTargets, 0, length);
}
else
updatedTargets = new String[OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length);
contextHandler.setProtectedTargets(updatedTargets);
return contextHandler;
}
}
public static class EE10WebAppFactory implements ContextFactory
{
private Bundle _myBundle;
public EE10WebAppFactory(Bundle bundle)
{
_myBundle = bundle;
}
@Override
public ContextHandler createContextHandler(AbstractContextProvider provider, App app)
throws Exception
{
OSGiApp osgiApp = OSGiApp.class.cast(app);
String jettyHome = (String)app.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME);
Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome));
WebAppContext webApp = new WebAppContext();
//Apply defaults from the deployer providers
webApp.initializeDefaults(provider.getProperties());
// provides access to core classes
ClassLoader coreLoader = (ClassLoader)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER);
if (LOG.isDebugEnabled())
LOG.debug("Core classloader = {}", coreLoader);
//provide access to all ee10 classes
ClassLoader environmentLoader = new OSGiClassLoader(coreLoader, _myBundle);
if (LOG.isDebugEnabled())
LOG.debug("Environment classloader = {}", environmentLoader);
//Ensure Configurations.getKnown is called with a classloader that can see all of the ee10 and core classes
ClassLoader old = Thread.currentThread().getContextClassLoader();
try
{
Thread.currentThread().setContextClassLoader(environmentLoader);
WebAppClassLoader.runWithServerClassAccess(() ->
{
Configurations.getKnown();
return null;
});
}
finally
{
Thread.currentThread().setContextClassLoader(old);
}
webApp.setConfigurations(Configurations.getKnown().stream()
.filter(c -> c.isEnabledByDefault())
.toArray(Configuration[]::new));
//Make a webapp classloader
OSGiWebappClassLoader webAppLoader = new OSGiWebappClassLoader(environmentLoader, webApp, osgiApp.getBundle());
//Handle Require-TldBundle
//This is a comma separated list of names of bundles that contain tlds that this webapp uses.
//We add them to the webapp classloader.
String requireTldBundles = (String)osgiApp.getProperties().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
List<Path> pathsToTldBundles = Util.getPathsToBundlesBySymbolicNames(requireTldBundles, osgiApp.getBundle().getBundleContext());
for (Path p : pathsToTldBundles)
webAppLoader.addClassPath(p.toUri().toString());
//Set up configuration from manifest headers
//extra classpath
String extraClasspath = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH);
if (extraClasspath != null)
webApp.setExtraClasspath(extraClasspath);
webApp.setClassLoader(webAppLoader);
//Take care of extra provider properties
webApp.setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, provider.getProperties().get(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN));
//TODO needed?
webApp.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundles);
//Set up some attributes
// rfc66
webApp.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT, osgiApp.getBundle().getBundleContext());
// spring-dm-1.2.1 looks for the BundleContext as a different attribute.
// not a spec... but if we want to support
// org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext
// then we need to do this to:
webApp.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(), osgiApp.getBundle().getBundleContext());
// also pass the bundle directly. sometimes a bundle does not have a
// bundlecontext.
// it is still useful to have access to the Bundle from the servlet
// context.
webApp.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, osgiApp.getBundle());
// apply any META-INF/context.xml file that is found to configure
// the webapp first
//First try looking for one in /META-INF
URI tmpUri = null;
URL contextXmlURL = Util.getLocalizedEntry("/META-INF/jetty-webapp-context.xml", osgiApp.getBundle());
if (contextXmlURL != null)
tmpUri = contextXmlURL.toURI();
//Then look in the property OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH and apply the first one
if (contextXmlURL == null)
{
String tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
if (tmp != null)
{
String[] filenames = tmp.split("[,;]");
tmpUri = Util.resolvePathAsLocalizedURI(filenames[0], osgiApp.getBundle(), jettyHomePath);
}
}
//apply a context xml if there is one
if (tmpUri != null)
{
final URI contextXmlUri = tmpUri;
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
try
{
Thread.currentThread().setContextClassLoader(webApp.getClassLoader());
WebAppClassLoader.runWithServerClassAccess(() ->
{
XmlConfiguration xmlConfiguration = new XmlConfiguration(ResourceFactory.of(webApp).newResource(contextXmlUri));
WebAppClassLoader.runWithServerClassAccess(() ->
{
Map<String, String> properties = new HashMap<>();
xmlConfiguration.getIdMap().put("Server", osgiApp.getDeploymentManager().getServer());
properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString());
properties.put(OSGiServerConstants.JETTY_HOME, (String)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME));
xmlConfiguration.getProperties().putAll(properties);
xmlConfiguration.configure(webApp);
return null;
});
return null;
});
}
catch (Exception e)
{
LOG.warn("Error applying context xml", e);
throw e;
}
finally
{
Thread.currentThread().setContextClassLoader(oldLoader);
}
}
//ensure the context path is set
webApp.setContextPath(osgiApp.getContextPath());
//osgi Enterprise Spec r4 p.427
webApp.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, osgiApp.getBundle().getBundleContext());
//Indicate the webapp has been deployed, so that we don't try and redeploy again
webApp.setAttribute(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK);
//make sure we protect also the osgi dirs specified by OSGi Enterprise spec
String[] targets = webApp.getProtectedTargets();
String[] updatedTargets = null;
if (targets != null)
{
updatedTargets = new String[targets.length + OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
System.arraycopy(targets, 0, updatedTargets, 0, targets.length);
}
else
updatedTargets = new String[OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length];
System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, targets.length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length);
webApp.setProtectedTargets(updatedTargets);
Path bundlePath = osgiApp.getPath();
Resource bundleResource = osgiApp.getBundleResource();
String pathToResourceBase = osgiApp.getPathToResourceBase();
//if the path wasn't set or it was ., then it is the root of the bundle's installed location
if (StringUtil.isBlank(pathToResourceBase) || ".".equals(pathToResourceBase))
{
if (LOG.isDebugEnabled())
LOG.debug("Webapp base using bundle install location: {}", bundleResource);
webApp.setWarResource(bundleResource);
}
else
{
if (pathToResourceBase.startsWith("/") || pathToResourceBase.startsWith("file:"))
{
//The baseResource is outside of the bundle
Path p = Paths.get(pathToResourceBase);
webApp.setWar(p.toUri().toString());
if (LOG.isDebugEnabled())
LOG.debug("Webapp base using absolute location: {}", p);
}
else
{
//The baseResource is relative to the root of the bundle
Resource r = bundleResource.resolve(pathToResourceBase);
webApp.setWarResource(r);
if (LOG.isDebugEnabled())
LOG.debug("Webapp base using path relative to bundle unpacked install location: {}", r);
}
}
//web.xml
String tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_WEB_XML_PATH);
if (!StringUtil.isBlank(tmp))
{
URI webXml = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath);
if (webXml != null)
webApp.setDescriptor(webXml.toString());
}
// webdefault-ee10.xml
tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH);
if (tmp != null)
{
URI defaultWebXml = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath);
if (defaultWebXml != null)
{
webApp.setDefaultsDescriptor(defaultWebXml.toString());
}
}
return webApp;
}
}
private PackageAdminServiceTracker _packageAdminServiceTracker;
private ServiceTracker<Server, Object> _tracker;
/**
* Track jetty Server instances and add ability to deploy EE10 contexts/webapps
*
* @param context the bundle context
*/
@Override
public void start(final BundleContext context) throws Exception
{
// track other bundles and fragments attached to this bundle that we
// should activate.
_packageAdminServiceTracker = new PackageAdminServiceTracker(context);
//track jetty Server instances
_tracker = new ServiceTracker<Server, Object>(context, context.createFilter("(objectclass=" + Server.class.getName() + ")"), new ServerTracker(context.getBundle()));
_tracker.open();
//register for bundleresource: url resource handling
ResourceFactory.registerResourceFactory("bundleresource", new URLResourceFactory());
}
/**
* Stop the activator.
*
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
@Override
public void stop(BundleContext context) throws Exception
{
if (_tracker != null)
{
_tracker.close();
_tracker = null;
}
}
}

View File

@ -1,126 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot;
import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHomeHelper;
import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.JettyServerServiceTracker;
import org.eclipse.jetty.ee10.osgi.boot.utils.internal.PackageAdminServiceTracker;
import org.eclipse.jetty.server.Server;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* JettyBootstrapActivator
* <p>
* Bootstrap jetty and publish a default Server instance as an OSGi service.
* <p>
* Listen for other Server instances to be published as services and support them as deployment targets.
* <p>
* Listen for Bundles to be activated, and deploy those that represent webapps/ContextHandlers to one of the known Server instances.
*/
public class JettyBootstrapActivator implements BundleActivator
{
private static final Logger LOG = LoggerFactory.getLogger(JettyBootstrapActivator.class);
private static JettyBootstrapActivator INSTANCE = null;
public static JettyBootstrapActivator getInstance()
{
return INSTANCE;
}
private ServiceRegistration _registeredServer;
private PackageAdminServiceTracker _packageAdminServiceTracker;
private ServiceTracker _jettyServerServiceTracker;
/**
* Setup a new jetty Server, registers it as a service. Setup the Service
* tracker for the jetty ContextHandlers that are in charge of deploying the
* webapps. Setup the BundleListener that supports the extender pattern for
* the jetty ContextHandler.
*
* @param context the bundle context
*/
@Override
public void start(final BundleContext context) throws Exception
{
ServiceReference[] references = context.getAllServiceReferences("org.eclipse.jetty.http.HttpFieldPreEncoder", null);
if (references == null || references.length == 0)
LOG.warn("OSGi support for java.util.ServiceLoader may not be present. You may experience runtime errors.");
INSTANCE = this;
// track other bundles and fragments attached to this bundle that we
// should activate.
_packageAdminServiceTracker = new PackageAdminServiceTracker(context);
// track jetty Server instances that we should support as deployment targets
_jettyServerServiceTracker = new ServiceTracker(context, context.createFilter("(objectclass=" + Server.class.getName() + ")"), new JettyServerServiceTracker());
_jettyServerServiceTracker.open();
// Create a default jetty instance right now.
DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(context);
}
/**
* Stop the activator.
*
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
@Override
public void stop(BundleContext context) throws Exception
{
try
{
if (_jettyServerServiceTracker != null)
{
_jettyServerServiceTracker.close();
_jettyServerServiceTracker = null;
}
if (_packageAdminServiceTracker != null)
{
_packageAdminServiceTracker.stop();
context.removeServiceListener(_packageAdminServiceTracker);
_packageAdminServiceTracker = null;
}
if (_registeredServer != null)
{
try
{
_registeredServer.unregister();
}
catch (IllegalArgumentException ill)
{
// already unregistered.
}
finally
{
_registeredServer = null;
}
}
}
finally
{
INSTANCE = null;
}
}
}

View File

@ -1,77 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.bindings.StandardDeployer;
import org.eclipse.jetty.deploy.graph.Node;
import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.ee10.osgi.boot.utils.EventSender;
/**
* OSGiDeployer
*
* Extension of standard Jetty deployer that emits OSGi EventAdmin
* events whenever a webapp is deployed into OSGi via Jetty.
*/
public class OSGiDeployer extends StandardDeployer
{
private ServerInstanceWrapper _server;
public OSGiDeployer(ServerInstanceWrapper server)
{
_server = server;
}
@Override
public void processBinding(Node node, App app) throws Exception
{
//TODO how to NOT send this event if its not a webapp:
//OSGi Enterprise Spec only wants an event sent if its a webapp bundle (ie not a ContextHandler)
if (!(app instanceof AbstractOSGiApp))
{
doProcessBinding(node, app);
}
else
{
EventSender.getInstance().send(EventSender.DEPLOYING_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
try
{
doProcessBinding(node, app);
((AbstractOSGiApp)app).registerAsOSGiService();
EventSender.getInstance().send(EventSender.DEPLOYED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
}
catch (Exception e)
{
EventSender.getInstance().send(EventSender.FAILED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
throw e;
}
}
}
protected void doProcessBinding(Node node, App app) throws Exception
{
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(_server.getParentClassLoaderForWebapps());
try
{
super.processBinding(node, app);
}
finally
{
Thread.currentThread().setContextClassLoader(old);
}
}
}

View File

@ -27,34 +27,34 @@ import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.regex.Pattern;
import org.eclipse.jetty.ee10.osgi.boot.utils.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.ee10.osgi.boot.utils.Util;
import org.eclipse.jetty.ee10.osgi.boot.utils.internal.PackageAdminServiceTracker;
import org.eclipse.jetty.ee10.webapp.Configuration;
import org.eclipse.jetty.ee10.webapp.MetaInfConfiguration;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.ee10.webapp.WebInfConfiguration;
import org.eclipse.jetty.osgi.OSGiWebappConstants;
import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.osgi.util.Util;
import org.eclipse.jetty.util.FileID;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection;
import org.eclipse.jetty.util.resource.ResourceFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* OSGiWebInfConfiguration
* OSGiMetaInfConfiguration
*
* Handle adding resources found in bundle fragments, and add them into the
* Handle adding resources found in bundles.
*/
public class OSGiMetaInfConfiguration extends MetaInfConfiguration
{
private static final Logger LOG = LoggerFactory.getLogger(WebInfConfiguration.class);
private static final Logger LOG = LoggerFactory.getLogger(OSGiMetaInfConfiguration.class);
/**
* Comma separated list of symbolic names of bundles that contain tlds that should be considered
* as on the container classpath
*/
public static final String SYS_PROP_TLD_BUNDLES = "org.eclipse.jetty.ee10.osgi.tldbundles";
public static final String SYS_PROP_TLD_BUNDLES = "org.eclipse.jetty.osgi.tldbundles";
/**
* Regex of symbolic names of bundles that should be considered to be on the container classpath
*/
@ -108,13 +108,15 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration
names.add(tokenizer.nextToken());
}
}
HashSet<Resource> matchingResources = new HashSet<Resource>();
HashSet<Resource> matchingResources = new HashSet<>();
if (!names.isEmpty() || pattern != null)
{
Bundle[] bundles = FrameworkUtil.getBundle(OSGiMetaInfConfiguration.class).getBundleContext().getBundles();
for (Bundle bundle : bundles)
{
if (LOG.isDebugEnabled())
LOG.debug("Checking bundle {}:{}", bundle.getBundleId(), bundle.getSymbolicName());
if (pattern != null)
{
@ -122,14 +124,14 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration
if (pattern.matcher(bundle.getSymbolicName()).matches())
{
//get the file location of the jar and put it into the list of container jars that will be scanned for stuff (including tlds)
matchingResources.addAll(getBundleAsResource(bundle));
matchingResources.addAll(getBundleAsResource(ResourceFactory.of(context), bundle));
}
}
if (names != null)
{
//if there is an explicit bundle name, then check if it matches
if (names.contains(bundle.getSymbolicName()))
matchingResources.addAll(getBundleAsResource(bundle));
matchingResources.addAll(getBundleAsResource(ResourceFactory.of(context), bundle));
}
}
}
@ -168,6 +170,7 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration
Bundle[] bundles = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles((Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE));
if (bundles != null && bundles.length > 0)
{
@SuppressWarnings("unchecked")
Set<Bundle> fragsAndReqsBundles = (Set<Bundle>)context.getAttribute(FRAGMENT_AND_REQUIRED_BUNDLES);
if (fragsAndReqsBundles == null)
{
@ -175,6 +178,7 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration
context.setAttribute(FRAGMENT_AND_REQUIRED_BUNDLES, fragsAndReqsBundles);
}
@SuppressWarnings("unchecked")
Set<Resource> fragsAndReqsResources = (Set<Resource>)context.getAttribute(FRAGMENT_AND_REQUIRED_RESOURCES);
if (fragsAndReqsResources == null)
{
@ -191,7 +195,7 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration
//add to context attribute storing associated fragments and required bundles
fragsAndReqsBundles.add(b);
File f = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(b);
Resource r = Resource.newResource(f.toURI());
Resource r = ResourceFactory.of(context).newResource(f.toURI());
//add to convenience context attribute storing fragments and required bundles as Resources
fragsAndReqsResources.add(r);
mergedResources.add(r);
@ -217,6 +221,7 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration
Bundle bundle = (Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
if (bundle != null)
{
@SuppressWarnings("unchecked")
Set<Bundle> fragments = (Set<Bundle>)context.getAttribute(FRAGMENT_AND_REQUIRED_BUNDLES);
if (fragments != null && !fragments.isEmpty())
{
@ -236,14 +241,15 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration
for (Bundle frag : fragments)
{
String path = Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_FRAGMENT_RESOURCE_PATH, frag.getHeaders());
convertFragmentPathToResource(path, frag, appendedResourcesPath);
convertFragmentPathToResource(ResourceFactory.of(context), path, frag, appendedResourcesPath);
path = Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_PREPEND_FRAGMENT_RESOURCE_PATH, frag.getHeaders());
convertFragmentPathToResource(path, frag, prependedResourcesPath);
convertFragmentPathToResource(ResourceFactory.of(context), path, frag, prependedResourcesPath);
}
if (!appendedResourcesPath.isEmpty())
{
LinkedHashSet<Resource> resources = new LinkedHashSet<Resource>();
LinkedHashSet<Resource> resources = new LinkedHashSet<>();
//Add in any existing setting of extra resource dirs
@SuppressWarnings("unchecked")
Set<Resource> resourceDirs = (Set<Resource>)context.getAttribute(MetaInfConfiguration.RESOURCE_DIRS);
if (resourceDirs != null && !resourceDirs.isEmpty())
resources.addAll(resourceDirs);
@ -263,8 +269,7 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration
Resource[] resources = new Resource[1 + prependedResourcesPath.size()];
System.arraycopy(prependedResourcesPath.values().toArray(new Resource[prependedResourcesPath.size()]), 0, resources, 0, prependedResourcesPath.size());
resources[resources.length - 1] = context.getBaseResource();
context.setBaseResource(Resource.of(resources));
context.setBaseResource(ResourceFactory.combine(resources));
}
}
@ -272,10 +277,10 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration
* Resolves the bundle. Usually that would be a single URL per bundle. But we do some more work if there are jars
* embedded in the bundle.
*/
private List<Resource> getBundleAsResource(Bundle bundle)
private List<Resource> getBundleAsResource(ResourceFactory resourceFactory, Bundle bundle)
throws Exception
{
List<Resource> resources = new ArrayList<Resource>();
List<Resource> resources = new ArrayList<>();
File file = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle);
if (file.isDirectory())
@ -284,7 +289,7 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration
{
if (FileID.isJavaArchive(f.getName()) && f.isFile())
{
resources.add(Resource.newResource(f));
resources.add(resourceFactory.newResource(f.toPath()));
}
else if (f.isDirectory() && f.getName().equals("lib"))
{
@ -292,16 +297,16 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration
{
if (FileID.isJavaArchive(f2.getName()) && f2.isFile())
{
resources.add(Resource.newResource(f));
resources.add(resourceFactory.newResource(f.toPath()));
}
}
}
}
resources.add(Resource.newResource(file)); //TODO really???
resources.add(resourceFactory.newResource(file.toPath())); //TODO really???
}
else
{
resources.add(Resource.newResource(file));
resources.add(resourceFactory.newResource(file.toPath()));
}
return resources;
@ -310,7 +315,7 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration
/**
* Convert a path inside a fragment into a Resource
*/
private void convertFragmentPathToResource(String resourcePath, Bundle fragment, Map<String, Resource> resourceMap)
private void convertFragmentPathToResource(ResourceFactory resourceFactory, String resourcePath, Bundle fragment, Map<String, Resource> resourceMap)
throws Exception
{
if (resourcePath == null)
@ -333,6 +338,6 @@ public class OSGiMetaInfConfiguration extends MetaInfConfiguration
{
uri = new URI(url.toString().replaceAll(" ", "%20"));
}
resourceMap.put(key + ";" + fragment.getSymbolicName(), Resource.newResource(uri));
resourceMap.put(key + ";" + fragment.getSymbolicName(), resourceFactory.newResource(uri));
}
}

View File

@ -1,83 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot;
/**
* OSGiServerConstants
*
* Name of the properties that configure a jetty Server OSGi service.
*/
public class OSGiServerConstants
{
/**
* Usual system property used as the hostname for a typical jetty
* configuration.
*/
public static final String JETTY_HOME = "jetty.home";
public static final String JETTY_BASE = "jetty.base";
/**
* System property to point to a bundle that embeds a jetty configuration
* and that jetty configuration should be the default jetty server. First we
* look for jetty.home. If we don't find it then we look for this property.
*/
public static final String JETTY_HOME_BUNDLE = "jetty.home.bundle";
/**
* Usual system property used as the hostname for a typical jetty
* configuration.
*/
public static final String JETTY_HOST = "jetty.http.host";
/**
* Usual system property used as the port for http for a typical jetty
* configuration.
*/
public static final String JETTY_PORT = "jetty.http.port";
/**
* Usual system property used as the port for https for a typical jetty
* configuration.
*/
public static final String JETTY_PORT_SSL = "jetty.ssl.port";
//for managed jetty instances, name of the configuration parameters
/**
* PID of the jetty servers's ManagedFactory
*/
public static final String MANAGED_JETTY_SERVER_FACTORY_PID = "org.eclipse.jetty.ee10.osgi.boot.managedserverfactory";
/**
* The associated value of that configuration parameter is the name under which this
* instance of the jetty server is tracked.
* When a ContextHandler is deployed and it specifies the managedServerName property, it is deployed
* on the corresponding jetty managed server or it throws an exception: jetty server not available.
*/
public static final String MANAGED_JETTY_SERVER_NAME = "managedServerName";
/**
* Name of the 'default' jetty server instance.
* Usually the first one to be created.
*/
public static final String MANAGED_JETTY_SERVER_DEFAULT_NAME = "defaultJettyServer";
/**
* List of URLs to the jetty.xml files that configure th server.
*/
public static final String MANAGED_JETTY_XML_CONFIG_URLS = "jetty.etc.config.urls";
/**
* List of URLs to the folders where the legacy J2EE shared libraries are stored aka lib/ext, lib/jsp etc.
*/
public static final String MANAGED_JETTY_SHARED_LIB_FOLDER_URLS = "managedJettySharedLibFolderUrls";
}

View File

@ -1,54 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.bindings.StandardUndeployer;
import org.eclipse.jetty.deploy.graph.Node;
import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.ee10.osgi.boot.utils.EventSender;
/**
* OSGiUndeployer
*
* Extension of the Jetty Undeployer which emits OSGi EventAdmin events
* whenever a webapp is undeployed from Jetty.
*/
public class OSGiUndeployer extends StandardUndeployer
{
private ServerInstanceWrapper _server;
public OSGiUndeployer(ServerInstanceWrapper server)
{
_server = server;
}
@Override
public void processBinding(Node node, App app) throws Exception
{
EventSender.getInstance().send(EventSender.UNDEPLOYING_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(_server.getParentClassLoaderForWebapps());
try
{
super.processBinding(node, app);
}
finally
{
Thread.currentThread().setContextClassLoader(old);
}
EventSender.getInstance().send(EventSender.UNDEPLOYED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath());
((AbstractOSGiApp)app).deregisterAsOSGiService();
}
}

View File

@ -1,31 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot;
import org.eclipse.jetty.ee10.webapp.Configuration;
import org.eclipse.jetty.ee10.webapp.WebInfConfiguration;
/**
* OSGiWebInfConfiguration
*
* Handle adding resources found in bundle fragments, and add them into the
*/
public class OSGiWebInfConfiguration extends WebInfConfiguration
{
@Override
public Class<? extends Configuration> replaces()
{
return WebInfConfiguration.class;
}
}

View File

@ -11,24 +11,15 @@
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.internal.webapp;
package org.eclipse.jetty.ee10.osgi.boot;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarFile;
import jakarta.servlet.http.HttpServlet;
import org.eclipse.jetty.ee10.osgi.boot.utils.BundleClassLoaderHelperFactory;
import org.eclipse.jetty.ee10.webapp.WebAppClassLoader;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.osgi.util.BundleClassLoaderHelperFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleReference;
import org.slf4j.Logger;
@ -42,30 +33,8 @@ import org.slf4j.LoggerFactory;
*/
public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleReference
{
private static final Logger LOG = LoggerFactory.getLogger(OSGiWebappClassLoader.class.getName());
/**
* when a logging framework is setup in the osgi classloaders, it can access
* this and register the classes that must not be found in the jar.
*/
public static final Set<String> JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED = new HashSet<>();
public static void addClassThatIdentifiesAJarThatMustBeRejected(Class<?> zclass)
{
JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(TypeUtil.toClassReference(zclass.getName()));
}
public static void addClassThatIdentifiesAJarThatMustBeRejected(String zclassName)
{
JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(TypeUtil.toClassReference(zclassName));
}
static
{
addClassThatIdentifiesAJarThatMustBeRejected(HttpServlet.class);
}
private ClassLoader _osgiBundleClassLoader;
private Bundle _contributor;
@ -171,91 +140,4 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe
}
}
}
private List<URL> toList(Enumeration<URL> e, Enumeration<URL> e2)
{
List<URL> list = new ArrayList<>();
while (e != null && e.hasMoreElements())
{
list.add(e.nextElement());
}
while (e2 != null && e2.hasMoreElements())
{
list.add(e2.nextElement());
}
return list;
}
/**
* Parse the classpath ourselves to be able to filter things. This is a
* derivative work of the super class
*/
@Override
public void addClassPath(String classPath) throws IOException
{
for (Resource resource : Resource.fromList(classPath, false, (path) -> getContext().newResource(path)))
{
File file = resource.getFile();
if (file != null && isAcceptableLibrary(file, JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED))
{
super.addClassPath(resource);
}
else
{
LOG.info("Did not add {} to the classloader of the webapp {}", resource, getContext());
}
}
}
/**
* @return true if the lib should be included in the webapp classloader.
*/
private boolean isAcceptableLibrary(File file, Set<String> pathToClassFiles)
{
try
{
if (file.isDirectory())
{
for (String criteria : pathToClassFiles)
{
if (new File(file, criteria).exists())
{
return false;
}
}
}
else
{
JarFile jar = null;
try
{
jar = new JarFile(file);
for (String criteria : pathToClassFiles)
{
if (jar.getEntry(criteria) != null)
{
return false;
}
}
}
finally
{
if (jar != null)
try
{
jar.close();
}
catch (IOException ignored)
{
}
}
}
}
catch (IOException e)
{
// nevermind. just trying our best
LOG.trace("IGNORED", e);
}
return true;
}
}

View File

@ -1,138 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot;
/**
* OSGiWebappConstants
*
*
* Constants (MANIFEST headers, service properties etc) associated with deploying
* webapps into OSGi via Jetty.
*/
public class OSGiWebappConstants
{
/**
* service property osgi.web.symbolicname. See OSGi r4
*/
public static final String OSGI_WEB_SYMBOLICNAME = "osgi.web.symbolicname";
/**
* service property osgi.web.symbolicname. See OSGi r4
*/
public static final String OSGI_WEB_VERSION = "osgi.web.version";
/**
* service property osgi.web.contextpath. See OSGi r4
*/
public static final String OSGI_WEB_CONTEXTPATH = "osgi.web.contextpath";
/**
* See OSGi r4 p.427
*/
public static final String OSGI_BUNDLECONTEXT = "osgi-bundlecontext";
/**
* url scheme to deploy war file as bundled webapp
*/
public static final String RFC66_WAR_URL_SCHEME = "war";
/**
* Name of the header that defines the context path for the embedded webapp.
*/
public static final String RFC66_WEB_CONTEXTPATH = "Web-ContextPath";
/**
* Name of the header that defines the path to the folder where the jsp
* files are extracted.
*/
public static final String RFC66_JSP_EXTRACT_LOCATION = "Jsp-ExtractLocation";
/**
* Name of the servlet context attribute that points to the bundle context.
*/
public static final String RFC66_OSGI_BUNDLE_CONTEXT = "osgi-bundlecontext";
/**
* Name of the servlet context attribute that points to the bundle object.
* We can't always rely on the bundle-context as there might be no such thing.
*/
public static final String JETTY_OSGI_BUNDLE = "osgi-bundle";
/**
* List of relative pathes within the bundle to the jetty context files.
*/
public static final String JETTY_CONTEXT_FILE_PATH = "Jetty-ContextFilePath";
/**
* path within the bundle to the folder that contains the basic resources.
*/
public static final String JETTY_WAR_RESOURCE_PATH = "Jetty-WarResourcePath";
/**
* path within a fragment hosted by a web-bundle to a folder that contains basic resources.
* the path is appended to the lookup path where jetty locates static resources
*/
public static final String JETTY_WAR_FRAGMENT_RESOURCE_PATH = "Jetty-WarFragmentResourcePath";
/**
* path within a fragment hosted by a web-bundle to a folder that contains basic resources.
* The path is prefixed to the lookup path where jetty locates static resources:
* this will override static resources with the same name in the web-bundle.
*/
public static final String JETTY_WAR_PREPEND_FRAGMENT_RESOURCE_PATH = "Jetty-WarPrependFragmentResourcePath";
/**
* installation path of webapp bundle
*/
public static final String JETTY_BUNDLE_ROOT = "bundle.root";
/**
* Extra classpath
*/
public static final String JETTY_EXTRA_CLASSPATH = "Jetty-extraClasspath";
/**
* web.xml file path
*/
public static final String JETTY_WEB_XML_PATH = "Jetty-WebXmlFilePath";
/**
* defaultweb.xml file path
*/
public static final String JETTY_DEFAULT_WEB_XML_PATH = "Jetty-defaultWebXmlFilePath";
/**
* path to the base folder that overrides the computed bundle installation
* location if not null useful to install webapps or jetty context files
* that are in fact not embedded in a bundle
*/
public static final String JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE = "Jetty-bundleInstall";
/**
* Comma separated list of bundles that contain tld file used by the webapp.
*/
public static final String REQUIRE_TLD_BUNDLE = "Require-TldBundle";
/**
* Comma separated list of bundles that contain tld file used by the webapp.
* Both the name of the manifest header and the name of the service property.
*/
public static final String SERVICE_PROP_REQUIRE_TLD_BUNDLE = REQUIRE_TLD_BUNDLE;
public static final String WATERMARK = "o.e.j.o.b.watermark";
/**
* Set of extra dirs that must not be served by osgi webapps
*/
public static final String[] DEFAULT_PROTECTED_OSGI_TARGETS = {"/osgi-inf", "/osgi-opts"};
}

View File

@ -11,7 +11,7 @@
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.utils.internal;
package org.eclipse.jetty.ee10.osgi.boot;
import java.util.ArrayList;
import java.util.LinkedHashMap;
@ -28,6 +28,8 @@ import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.service.startlevel.StartLevel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* PackageAdminServiceTracker
@ -35,11 +37,12 @@ import org.osgi.service.startlevel.StartLevel;
* When the PackageAdmin service is activated we can look for the fragments
* attached to this bundle and do a fake "activate" on them.
* <p>
* See particularly the jetty-ee10-osgi-boot-jsp fragment bundle that uses this
* See particularly the jetty-ee9-osgi-boot-jsp fragment bundle that uses this
* facility.
*/
public class PackageAdminServiceTracker implements ServiceListener
{
private static Logger LOG = LoggerFactory.getLogger(PackageAdminServiceTracker.class);
private BundleContext _context;
private List<BundleActivator> _activatedFragments = new ArrayList<>();
@ -55,26 +58,21 @@ public class PackageAdminServiceTracker implements ServiceListener
public static PackageAdminServiceTracker INSTANCE = null;
public PackageAdminServiceTracker(BundleContext context)
throws Exception
{
INSTANCE = this;
_context = context;
if (!setup())
{
try
{
_context.addServiceListener(this, "(objectclass=" + PackageAdmin.class.getName() + ")");
}
catch (InvalidSyntaxException e)
{
e.printStackTrace(); // won't happen
}
}
}
/**
* @return true if the fragments were activated by this method.
*/
private boolean setup()
throws Exception
{
ServiceReference sr = _context.getServiceReference(PackageAdmin.class.getName());
_fragmentsWereActivated = sr != null;
@ -110,9 +108,16 @@ public class PackageAdminServiceTracker implements ServiceListener
public void serviceChanged(ServiceEvent event)
{
if (event.getType() == ServiceEvent.REGISTERED)
{
try
{
invokeFragmentActivators(event.getServiceReference());
}
catch (Exception e)
{
LOG.warn("Error invoking fragment activators", e);
}
}
}
/**
@ -271,6 +276,7 @@ public class PackageAdminServiceTracker implements ServiceListener
}
private void invokeFragmentActivators(ServiceReference sr)
throws Exception
{
PackageAdmin admin = (PackageAdmin)_context.getService(sr);
Bundle[] fragments = admin.getFragments(_context.getBundle());
@ -281,8 +287,6 @@ public class PackageAdminServiceTracker implements ServiceListener
for (Bundle frag : fragments)
{
// find a convention to look for a class inside the fragment.
try
{
String fragmentActivator = frag.getSymbolicName() + ".FragmentActivator";
Class<?> c = Class.forName(fragmentActivator);
if (c != null)
@ -292,11 +296,6 @@ public class PackageAdminServiceTracker implements ServiceListener
_activatedFragments.add(bActivator);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
public void stop()

View File

@ -1,224 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.ee10.osgi.boot.utils.Util;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ServiceContextProvider
*
* Jetty DeploymentManager Provider that is able to deploy ContextHandlers discovered via OSGi as services.
*/
public class ServiceContextProvider extends AbstractContextProvider implements ServiceProvider
{
private static final Logger LOG = LoggerFactory.getLogger(AbstractContextProvider.class);
private Map<ServiceReference, App> _serviceMap = new HashMap<>();
private ServiceRegistration _serviceRegForServices;
ServiceTracker _tracker;
/**
* ContextTracker
*/
public class ContextTracker extends ServiceTracker
{
public ContextTracker(BundleContext bundleContext, Filter filter)
{
super(bundleContext, filter, null);
}
@Override
public Object addingService(ServiceReference reference)
{
ContextHandler h = (ContextHandler)context.getService(reference);
serviceAdded(reference, h);
return h;
}
@Override
public void modifiedService(ServiceReference reference, Object service)
{
removedService(reference, service);
addingService(reference);
}
@Override
public void removedService(ServiceReference reference, Object service)
{
context.ungetService(reference);
serviceRemoved(reference, (ContextHandler)service);
}
}
/**
* ServiceApp
*/
public class ServiceApp extends OSGiApp
{
public ServiceApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String contextFile, String originId)
{
super(manager, provider, bundle, properties, contextFile, originId);
}
public ServiceApp(DeploymentManager manager, AppProvider provider, String originId, Bundle bundle, String contextFile)
{
super(manager, provider, originId, bundle, contextFile);
}
@Override
public void registerAsOSGiService() throws Exception
{
//not applicable for apps that are already services
}
@Override
protected void deregisterAsOSGiService() throws Exception
{
//not applicable for apps that are already services
}
}
public ServiceContextProvider(ServerInstanceWrapper wrapper)
{
super(wrapper);
}
@Override
public boolean serviceAdded(ServiceReference serviceRef, ContextHandler context)
{
if (context == null || serviceRef == null)
return false;
if (context instanceof org.eclipse.jetty.ee10.webapp.WebAppContext)
return false; //the ServiceWebAppProvider will deploy it
String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK);
if (watermark != null && !"".equals(watermark))
return false; //this service represents a contexthandler that has already been registered as a service by another of our deployers
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps());
try
{
//See if there is a context file to apply to this pre-made context
String contextFile = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH);
String[] keys = serviceRef.getPropertyKeys();
Dictionary<String, Object> properties = new Hashtable<>();
if (keys != null)
{
for (String key : keys)
{
properties.put(key, serviceRef.getProperty(key));
}
}
Bundle bundle = serviceRef.getBundle();
String originId = bundle.getSymbolicName() + "-" + bundle.getVersion().toString() + "-" + (contextFile != null ? contextFile : serviceRef.getProperty(Constants.SERVICE_ID));
ServiceApp app = new ServiceApp(getDeploymentManager(), this, bundle, properties, contextFile, originId);
app.setHandler(context); //set the pre=made ContextHandler instance
_serviceMap.put(serviceRef, app);
getDeploymentManager().addApp(app);
return true;
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
@Override
public boolean serviceRemoved(ServiceReference serviceRef, ContextHandler context)
{
if (context == null || serviceRef == null)
return false;
String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK);
if (watermark != null && !"".equals(watermark))
return false; //this service represents a contexthandler that will be deregistered as a service by another of our deployers
App app = _serviceMap.remove(serviceRef);
if (app != null)
{
getDeploymentManager().removeApp(app);
return true;
}
return false;
}
@Override
protected void doStart() throws Exception
{
BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
//Start a tracker to find webapps that are osgi services that are targeted to my server name
_tracker = new ContextTracker(bundleContext,
Util.createFilter(bundleContext, ContextHandler.class.getName(), getServerInstanceWrapper().getManagedServerName()));
_tracker.open();
//register as an osgi service for deploying contexts defined in a bundle, advertising the name of the jetty Server instance we are related to
Dictionary<String, String> properties = new Hashtable<>();
properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
//register as an osgi service for deploying contexts, advertising the name of the jetty Server instance we are related to
_serviceRegForServices = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(ServiceProvider.class.getName(), this, properties);
super.doStart();
}
@Override
protected void doStop() throws Exception
{
if (_tracker != null)
_tracker.close();
//unregister ourselves
if (_serviceRegForServices != null)
{
try
{
_serviceRegForServices.unregister();
}
catch (Exception e)
{
LOG.warn("Unable to unregister {}", _serviceRegForServices, e);
}
}
super.doStop();
}
}

View File

@ -1,29 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.osgi.framework.ServiceReference;
/**
* ServiceProvider
*
* Jetty DeploymentManager Provider api for webapps or ContextHandlers that are discovered as OSGi services.
*/
public interface ServiceProvider
{
public boolean serviceAdded(ServiceReference ref, ContextHandler handler) throws Exception;
public boolean serviceRemoved(ServiceReference ref, ContextHandler handler) throws Exception;
}

View File

@ -1,256 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper;
import org.eclipse.jetty.ee10.osgi.boot.utils.Util;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ServiceWebAppProvider
* <p>
* Jetty Provider that knows how to deploy a WebApp that has been registered as an OSGi service.
*/
public class ServiceWebAppProvider extends AbstractWebAppProvider implements ServiceProvider
{
private static final Logger LOG = LoggerFactory.getLogger(AbstractWebAppProvider.class);
/**
* Map of ServiceRef to App. Used when it is an osgi service that is a WebAppContext.
*/
private Map<ServiceReference, App> _serviceMap = new HashMap<>();
private ServiceRegistration _serviceRegForServices;
private ServiceTracker webappTracker;
/**
* WebAppTracker
*/
public class WebAppTracker extends ServiceTracker
{
/**
* @param bundleContext the osgi context
* @param filter the osgi filter for the tracker
*/
public WebAppTracker(BundleContext bundleContext, Filter filter)
{
super(bundleContext, filter, null);
}
@Override
public Object addingService(ServiceReference reference)
{
WebAppContext wac = (WebAppContext)context.getService(reference);
serviceAdded(reference, wac);
return wac;
}
@Override
public void modifiedService(ServiceReference reference, Object service)
{
removedService(reference, service);
addingService(reference);
}
@Override
public void removedService(ServiceReference reference, Object service)
{
serviceRemoved(reference, (WebAppContext)service);
context.ungetService(reference);
}
}
/**
* ServiceApp
*/
public class ServiceApp extends OSGiApp
{
public ServiceApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary<String, String> properties, String originId)
{
super(manager, provider, bundle, properties, originId);
}
public ServiceApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId)
{
super(manager, provider, bundle, originId);
}
@Override
public void registerAsOSGiService() throws Exception
{
//not applicable for apps that are already services
}
@Override
protected void deregisterAsOSGiService() throws Exception
{
//not applicable for apps that are already services
}
}
public ServiceWebAppProvider(ServerInstanceWrapper wrapper)
{
super(wrapper);
}
/**
* A webapp that was deployed as an osgi service has been added,
* and we want to deploy it.
*
* @param context the webapp
*/
@Override
public boolean serviceAdded(ServiceReference serviceRef, ContextHandler context)
{
if (context == null || !(context instanceof WebAppContext))
return false;
String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK);
if (watermark != null && !"".equals(watermark))
return false; //this service represents a webapp that has already been registered as a service by another of our deployers
WebAppContext webApp = (WebAppContext)context;
Dictionary<String, String> properties = new Hashtable<>();
String contextPath = (String)serviceRef.getProperty(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH);
if (contextPath == null)
return false; //No context path
String base = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_WAR_RESOURCE_PATH);
if (base == null)
return false; //No webapp base
String webdefaultXml = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH);
if (webdefaultXml != null)
properties.put(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH, webdefaultXml);
String webXml = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_WEB_XML_PATH);
if (webXml != null)
properties.put(OSGiWebappConstants.JETTY_WEB_XML_PATH, webXml);
String extraClassPath = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH);
if (extraClassPath != null)
properties.put(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH, extraClassPath);
String bundleInstallOverride = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE);
if (bundleInstallOverride != null)
properties.put(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE, bundleInstallOverride);
String requiredTlds = (String)serviceRef.getProperty(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
if (requiredTlds == null)
requiredTlds = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE);
if (requiredTlds != null)
properties.put(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requiredTlds);
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps());
try
{
String originId = getOriginId(serviceRef.getBundle(), base);
ServiceApp app = new ServiceApp(getDeploymentManager(), this, serviceRef.getBundle(), properties, originId);
app.setContextPath(contextPath);
app.setWebAppPath(base);
app.setWebAppContext(webApp); //set the pre=made webapp instance
_serviceMap.put(serviceRef, app);
getDeploymentManager().addApp(app);
return true;
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
/**
* @param context the webapp
*/
@Override
public boolean serviceRemoved(ServiceReference serviceRef, ContextHandler context)
{
if (context == null || !(context instanceof WebAppContext))
return false;
String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK);
if (watermark != null && !"".equals(watermark))
return false; //this service represents a contexthandler that will be deregistered as a service by another of our deployers
App app = _serviceMap.remove(serviceRef);
if (app != null)
{
getDeploymentManager().removeApp(app);
return true;
}
return false;
}
@Override
protected void doStart() throws Exception
{
BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
//Start a tracker to find webapps that are osgi services that are targeted to my server name
webappTracker = new WebAppTracker(bundleContext,
Util.createFilter(bundleContext, WebAppContext.class.getName(), getServerInstanceWrapper().getManagedServerName()));
webappTracker.open();
//register as an osgi service for deploying bundles, advertising the name of the jetty Server instance we are related to
Dictionary<String, String> properties = new Hashtable<>();
properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName());
//register as an osgi service for deploying contexts (discovered as osgi services), advertising the name of the jetty Server instance we are related to
_serviceRegForServices = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(ServiceProvider.class.getName(), this, properties);
super.doStart();
}
@Override
protected void doStop() throws Exception
{
webappTracker.close();
//unregister ourselves
if (_serviceRegForServices != null)
{
try
{
_serviceRegForServices.unregister();
}
catch (Exception e)
{
LOG.warn("Unable to unregister {}", _serviceRegForServices, e);
}
}
super.doStop();
}
}

View File

@ -1,92 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory;
import java.util.Dictionary;
import java.util.Hashtable;
import org.eclipse.jetty.ee10.osgi.boot.OSGiServerConstants;
import org.eclipse.jetty.server.Server;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* JettyServerServiceTracker
*
* Tracks instances of Jetty Servers, and configures them so that they can deploy
* webapps or ContextHandlers discovered from the OSGi environment.
*/
public class JettyServerServiceTracker implements ServiceTrackerCustomizer
{
private static Logger LOG = LoggerFactory.getLogger(JettyServerServiceTracker.class.getName());
@Override
public Object addingService(ServiceReference sr)
{
Bundle contributor = sr.getBundle();
Server server = (Server)contributor.getBundleContext().getService(sr);
String name = (String)sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
if (name == null)
{
throw new IllegalArgumentException("The property " + OSGiServerConstants.MANAGED_JETTY_SERVER_NAME + " is mandatory");
}
if (LOG.isDebugEnabled())
LOG.debug("Adding Server {}", name);
ServerInstanceWrapper wrapper = new ServerInstanceWrapper(name);
Dictionary<String, Object> props = new Hashtable<>();
for (String key : sr.getPropertyKeys())
{
props.put(key, sr.getProperty(key));
}
try
{
wrapper.start(server, props);
LOG.info("Started Server {}", name);
return wrapper;
}
catch (Exception e)
{
LOG.warn("Failed to start server {}", name, e);
return sr.getBundle().getBundleContext().getService(sr);
}
}
@Override
public void modifiedService(ServiceReference reference, Object service)
{
removedService(reference, service);
addingService(reference);
}
@Override
public void removedService(ServiceReference reference, Object service)
{
if (service instanceof ServerInstanceWrapper)
{
ServerInstanceWrapper wrapper = (ServerInstanceWrapper)service;
try
{
wrapper.stop();
LOG.info("Stopped Server {}", wrapper.getManagedServerName());
}
catch (Exception e)
{
LOG.warn("Failed to stop server {}", wrapper.getManagedServerName(), e);
}
}
}
}

View File

@ -1,441 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.eclipse.jetty.deploy.AppLifeCycle;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.deploy.bindings.StandardStarter;
import org.eclipse.jetty.deploy.bindings.StandardStopper;
import org.eclipse.jetty.ee10.osgi.boot.BundleContextProvider;
import org.eclipse.jetty.ee10.osgi.boot.BundleWebAppProvider;
import org.eclipse.jetty.ee10.osgi.boot.JettyBootstrapActivator;
import org.eclipse.jetty.ee10.osgi.boot.OSGiDeployer;
import org.eclipse.jetty.ee10.osgi.boot.OSGiServerConstants;
import org.eclipse.jetty.ee10.osgi.boot.OSGiUndeployer;
import org.eclipse.jetty.ee10.osgi.boot.ServiceContextProvider;
import org.eclipse.jetty.ee10.osgi.boot.ServiceWebAppProvider;
import org.eclipse.jetty.ee10.osgi.boot.internal.webapp.LibExtClassLoaderHelper;
import org.eclipse.jetty.ee10.osgi.boot.utils.BundleFileLocatorHelperFactory;
import org.eclipse.jetty.ee10.osgi.boot.utils.FakeURLClassLoader;
import org.eclipse.jetty.ee10.osgi.boot.utils.TldBundleDiscoverer;
import org.eclipse.jetty.ee10.osgi.boot.utils.Util;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ServerInstanceWrapper
*
* Configures and starts a jetty Server instance.
*/
public class ServerInstanceWrapper
{
/**
* The value of this property points to the parent director of the jetty.xml
* configuration file currently executed. Everything is passed as a URL to
* support the case where the bundle is zipped.
*/
public static final String PROPERTY_THIS_JETTY_XML_FOLDER_URL = "this.jetty.xml.parent.folder.url";
private static Collection<TldBundleDiscoverer> __containerTldBundleDiscoverers = new ArrayList<>();
private static final Logger LOG = LoggerFactory.getLogger(ServerInstanceWrapper.class.getName());
private final String _managedServerName;
/**
* The managed jetty server
*/
private Server _server;
private ContextHandlerCollection _ctxtCollection;
/**
* This is the class loader that should be the parent classloader of any
* webapp classloader. It is in fact the _libExtClassLoader with a trick to
* let the TldScanner find the jars where the tld files are.
*/
private ClassLoader _commonParentClassLoaderForWebapps;
private DeploymentManager _deploymentManager;
public static void addContainerTldBundleDiscoverer(TldBundleDiscoverer tldBundleDiscoverer)
{
__containerTldBundleDiscoverers.add(tldBundleDiscoverer);
}
public static Collection<TldBundleDiscoverer> getContainerTldBundleDiscoverers()
{
return __containerTldBundleDiscoverers;
}
public static Server configure(Server server, List<URL> jettyConfigurations, Dictionary<String, Object> props) throws Exception
{
if (jettyConfigurations == null || jettyConfigurations.isEmpty())
{
return server;
}
Map<String, Object> idMap = new HashMap<>();
if (server != null)
{
//Put in a mapping for the id "Server" and the name of the server as the instance being configured
idMap.put("Server", server);
idMap.put((String)props.get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME), server);
}
Map<String, String> properties = new HashMap<>();
if (props != null)
{
Enumeration<String> en = props.keys();
while (en.hasMoreElements())
{
String key = en.nextElement();
Object value = props.get(key);
properties.put(key, value.toString());
if (server != null)
server.setAttribute(key, value);
}
}
for (URL jettyConfiguration : jettyConfigurations)
{
try
{
// Execute a Jetty configuration file
XmlConfiguration config = new XmlConfiguration(Resource.newResource(jettyConfiguration));
config.getIdMap().putAll(idMap);
config.getProperties().putAll(properties);
// #334062 compute the URL of the folder that contains the
// conf file and set it as a property so we can compute relative paths
// from it.
String urlPath = jettyConfiguration.toString();
int lastSlash = urlPath.lastIndexOf('/');
if (lastSlash > 4)
{
urlPath = urlPath.substring(0, lastSlash);
config.getProperties().put(PROPERTY_THIS_JETTY_XML_FOLDER_URL, urlPath);
}
Object o = config.configure();
if (server == null)
server = (Server)o;
idMap = config.getIdMap();
}
catch (Exception e)
{
LOG.warn("Configuration error in {}", jettyConfiguration);
throw e;
}
}
return server;
}
public ServerInstanceWrapper(String managedServerName)
{
_managedServerName = managedServerName;
}
public String getManagedServerName()
{
return _managedServerName;
}
/**
* The classloader that should be the parent classloader for each webapp
* deployed on this server.
*
* @return the classloader
*/
public ClassLoader getParentClassLoaderForWebapps()
{
return _commonParentClassLoaderForWebapps;
}
/**
* @return The deployment manager registered on this server.
*/
public DeploymentManager getDeploymentManager()
{
return _deploymentManager;
}
/**
* @return The app provider registered on this server.
*/
public Server getServer()
{
return _server;
}
/**
* @return The collection of context handlers
*/
public ContextHandlerCollection getContextHandlerCollection()
{
return _ctxtCollection;
}
public void start(Server server, Dictionary<String, Object> props) throws Exception
{
_server = server;
ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
try
{
List<URL> sharedURLs = getManagedJettySharedLibFolderUrls(props);
// passing this bundle's classloader as the context classloader
// makes sure there is access to all the jetty's bundles
ClassLoader libExtClassLoader = LibExtClassLoaderHelper.createLibExtClassLoader(null, sharedURLs, JettyBootstrapActivator.class.getClassLoader());
if (LOG.isDebugEnabled())
LOG.debug("LibExtClassLoader = {}", libExtClassLoader);
Thread.currentThread().setContextClassLoader(libExtClassLoader);
String jettyConfigurationUrls = (String)props.get(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS);
List<URL> jettyConfigurations = jettyConfigurationUrls != null ? Util.fileNamesAsURLs(jettyConfigurationUrls, StringUtil.DEFAULT_DELIMS) : null;
_server = configure(server, jettyConfigurations, props);
init();
//if support for jsp is enabled, we need to convert locations of bundles that contain tlds into urls.
//these are tlds that we want jasper to treat as if they are on the container's classpath. Web bundles
//can use the Require-TldBundle MANIFEST header to name other tld-containing bundles that should be regarded
//as on the webapp classpath.
if (!__containerTldBundleDiscoverers.isEmpty())
{
Set<URL> urls = new HashSet<>();
//discover bundles with tlds that need to be on the container's classpath as URLs
for (TldBundleDiscoverer d : __containerTldBundleDiscoverers)
{
URL[] list = d.getUrlsForBundlesWithTlds(_deploymentManager, BundleFileLocatorHelperFactory.getFactory().getHelper());
if (list != null)
{
for (URL u : list)
{
urls.add(u);
}
}
}
_commonParentClassLoaderForWebapps = new FakeURLClassLoader(libExtClassLoader, urls.toArray(new URL[urls.size()]));
}
else
_commonParentClassLoaderForWebapps = libExtClassLoader;
if (LOG.isDebugEnabled())
LOG.debug("common classloader = {}", _commonParentClassLoaderForWebapps);
server.start();
}
catch (Exception e)
{
if (server != null)
{
try
{
server.stop();
}
catch (Exception x)
{
LOG.trace("IGNORED", x);
}
}
throw e;
}
finally
{
Thread.currentThread().setContextClassLoader(contextCl);
}
}
public void stop()
{
try
{
if (_server.isRunning())
{
_server.stop();
}
}
catch (Exception e)
{
LOG.warn("Failed to stop server", e);
}
}
/**
* Must be called after the server is configured.
*
* It is assumed the server has already been configured with the ContextHandlerCollection structure.
*/
private void init()
{
// Get the context handler
_ctxtCollection = (ContextHandlerCollection)_server.getDescendant(ContextHandlerCollection.class);
if (_ctxtCollection == null)
throw new IllegalStateException("ERROR: No ContextHandlerCollection configured in Server");
List<String> providerClassNames = new ArrayList<>();
// get a deployerManager and some providers
Collection<DeploymentManager> deployers = _server.getBeans(DeploymentManager.class);
if (deployers != null && !deployers.isEmpty())
{
_deploymentManager = deployers.iterator().next();
for (AppProvider provider : _deploymentManager.getAppProviders())
{
providerClassNames.add(provider.getClass().getName());
}
}
else
{
//add some kind of default
_deploymentManager = new DeploymentManager();
_deploymentManager.setContexts(_ctxtCollection);
_server.addBean(_deploymentManager);
}
_deploymentManager.setUseStandardBindings(false);
List<AppLifeCycle.Binding> deploymentLifeCycleBindings = new ArrayList<>();
deploymentLifeCycleBindings.add(new OSGiDeployer(this));
deploymentLifeCycleBindings.add(new StandardStarter());
deploymentLifeCycleBindings.add(new StandardStopper());
deploymentLifeCycleBindings.add(new OSGiUndeployer(this));
_deploymentManager.setLifeCycleBindings(deploymentLifeCycleBindings);
if (!providerClassNames.contains(BundleWebAppProvider.class.getName()))
{
// create it on the fly with reasonable default values.
try
{
BundleWebAppProvider webAppProvider = new BundleWebAppProvider(this);
_deploymentManager.addAppProvider(webAppProvider);
}
catch (Exception e)
{
LOG.warn("Failed to add BundleAppProvider to DeploymentManager", e);
}
}
if (!providerClassNames.contains(ServiceWebAppProvider.class.getName()))
{
// create it on the fly with reasonable default values.
try
{
ServiceWebAppProvider webAppProvider = new ServiceWebAppProvider(this);
_deploymentManager.addAppProvider(webAppProvider);
}
catch (Exception e)
{
LOG.warn("Failed to add ServiceWebAppProvider to DeploymentManager", e);
}
}
if (!providerClassNames.contains(BundleContextProvider.class.getName()))
{
try
{
BundleContextProvider contextProvider = new BundleContextProvider(this);
_deploymentManager.addAppProvider(contextProvider);
}
catch (Exception e)
{
LOG.warn("Failed to add BundleContextProvider to DeploymentManager", e);
}
}
if (!providerClassNames.contains(ServiceContextProvider.class.getName()))
{
try
{
ServiceContextProvider contextProvider = new ServiceContextProvider(this);
_deploymentManager.addAppProvider(contextProvider);
}
catch (Exception e)
{
LOG.warn("Failed to add ServiceContextProvider to DeploymentManager", e);
}
}
}
/**
* Get the Jetty Shared Lib Folder URLs in a form that is suitable for
* {@link LibExtClassLoaderHelper} to use.
*
* @param props the properties to look for the configuration in
* @return the list of URLs found, or null if none found
*/
private List<URL> getManagedJettySharedLibFolderUrls(Dictionary<String, Object> props)
{
String sharedURLs = (String)props.get(OSGiServerConstants.MANAGED_JETTY_SHARED_LIB_FOLDER_URLS);
if (StringUtil.isBlank(sharedURLs))
{
return null;
}
List<URL> libURLs = new ArrayList<>();
StringTokenizer tokenizer = new StringTokenizer(sharedURLs, StringUtil.DEFAULT_DELIMS, false);
while (tokenizer.hasMoreTokens())
{
String tok = tokenizer.nextToken();
try
{
URL url = new URL(tok);
url = BundleFileLocatorHelperFactory.getFactory().getHelper().getFileURL(url);
if (url.getProtocol().equals("file"))
{
libURLs.add(new URL("jar:" + url.toExternalForm() + "!/"));
}
else
{
if (LOG.isDebugEnabled())
LOG.debug("Unrecognized Jetty Shared Lib URL: {}", url);
}
}
catch (Throwable mfe)
{
LOG.warn("Unable to process legacy lib folder {}", tok, mfe);
}
}
return libURLs;
}
}

View File

@ -1,196 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.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<>();
/**
* @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<>();
File jettyResources = new File(jettyHome, "resources");
if (jettyResources.exists())
{
// make sure it contains something else than README:
Map<String, File> jettyResFiles = new HashMap<>();
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 (FileID.isJavaArchive(f.getName()))
{
// 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<>();
if (otherJarsOrFolder != null)
{
urls.addAll(otherJarsOrFolder);
}
if (jarsContainerOrJars != null)
{
for (File libExt : jarsContainerOrJars)
{
if (libExt.isDirectory())
{
for (File f : libExt.listFiles())
{
if (FileID.isJavaArchive(f.getName()))
{
// 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);
}
}
}

View File

@ -1,53 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.utils;
import org.eclipse.jetty.ee10.osgi.boot.utils.internal.DefaultBundleClassLoaderHelper;
import org.osgi.framework.Bundle;
/**
* BundleClassLoaderHelper
* <p>
* Is there a clean OSGi way to go from the Bundle object to the classloader of
* the Bundle ? You can certainly take a class inside the bundle and get the
* bundle's classloader that way. Getting the classloader directly from the
* bundle would be nice.
* <p>
* We could use fragments that are specific to each OSGi implementation. Using
* introspection here to keep packaging simple and avoid the multiplication of
* the jars.
* <p>
* The default implementation relies on introspection and supports equinox-3.5
* and felix-2.0.0
*/
public interface BundleClassLoaderHelper
{
/**
* The name of the custom implementation for this interface in a fragment.
*/
public static final String CLASS_NAME = "org.eclipse.jetty.ee10.osgi.boot.utils.BundleClassLoaderHelperImpl";
/**
* The default instance supports felix and equinox
*/
public static BundleClassLoaderHelper DEFAULT = new DefaultBundleClassLoaderHelper();
/**
* @param bundle the bundle
* @return The classloader of a given bundle. Assuming the bundle is
* started.
*/
public ClassLoader getBundleClassLoader(Bundle bundle);
}

View File

@ -1,56 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* BundleClassLoaderHelperFactory
*
* Get a class loader helper adapted for the particular osgi environment.
*/
public class BundleClassLoaderHelperFactory
{
private static final Logger LOG = LoggerFactory.getLogger(BundleClassLoaderHelperFactory.class);
private static BundleClassLoaderHelperFactory _instance = new BundleClassLoaderHelperFactory();
public static BundleClassLoaderHelperFactory getFactory()
{
return _instance;
}
private BundleClassLoaderHelperFactory()
{
}
public BundleClassLoaderHelper getHelper()
{
//use the default
BundleClassLoaderHelper helper = BundleClassLoaderHelper.DEFAULT;
try
{
//if a fragment has not provided their own impl
helper = (BundleClassLoaderHelper)Class.forName(BundleClassLoaderHelper.CLASS_NAME)
.getDeclaredConstructor().newInstance();
}
catch (Throwable t)
{
LOG.trace("IGNORED", t);
}
return helper;
}
}

View File

@ -1,118 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.utils;
import java.io.File;
import java.net.URL;
import java.util.Enumeration;
import org.eclipse.jetty.ee10.osgi.boot.utils.internal.DefaultFileLocatorHelper;
import org.osgi.framework.Bundle;
/**
* BundleFileLocatorHelper
* <p>
* From a bundle to its location on the filesystem. Assumes the bundle is not a
* jar.
*/
public interface BundleFileLocatorHelper
{
/**
* The name of the custom implementation for this interface in a fragment.
*/
public static final String CLASS_NAME = "org.eclipse.jetty.ee10.osgi.boot.utils.FileLocatorHelperImpl";
/**
* The default instance supports felix and equinox
*/
public static BundleFileLocatorHelper DEFAULT = new DefaultFileLocatorHelper();
/**
* Works with equinox, felix, nuxeo and probably more. Not exactly in the
* spirit of OSGi but quite necessary to support self-contained webapps and
* other situations.
* <p>
* Currently only works with bundles that are not jar.
*
* @param bundle The bundle
* @return Its installation location as a file.
* @throws Exception if unable to get the install location
*/
public File getBundleInstallLocation(Bundle bundle) throws Exception;
/**
* Locate a file inside a bundle.
*
* @param bundle the bundle
* @param path the path
* @return file the file object
* @throws Exception if unable to get the file
*/
public File getFileInBundle(Bundle bundle, String path) throws Exception;
/**
* If the bundle is a jar, returns the jar. If the bundle is a folder, look
* inside it and search for jars that it returns.
* <p>
* Good enough for our purpose (TldLocationsCache when it scans for tld
* files inside jars alone. In fact we only support the second situation for
* development purpose where the bundle was imported in pde and the classes
* kept in a jar.
*
* @param bundle the bundle
* @return The jar(s) file that is either the bundle itself, either the jars
* embedded inside it.
* @throws Exception if unable to locate the jars
*/
public File[] locateJarsInsideBundle(Bundle bundle) throws Exception;
/**
* Helper method equivalent to Bundle#getEntry(String entryPath) except that
* it searches for entries in the fragments by using the findEntries method.
*
* @param bundle the bundle
* @param entryPath the entry path
* @return null or all the entries found for that path.
*/
public Enumeration<URL> findEntries(Bundle bundle, String entryPath);
/**
* Only useful for equinox: on felix we get the <code>file://</code> or <code>jar://</code> url
* already. Other OSGi implementations have not been tested
* <p>
* Get a URL to the bundle entry that uses a common protocol (i.e. <code>file:</code>
* <code>jar:</code> or <code>http:</code> etc.).
*
* @param url the url
* @return a URL to the bundle entry that uses a common protocol
* @throws Exception if unable to get the local url
*/
public URL getLocalURL(URL url) throws Exception;
/**
* Only useful for equinox: on felix we get the <code>file://</code> url already. Other
* OSGi implementations have not been tested
* <p>
* Get a URL to the content of the bundle entry that uses the <code>file:</code>
* protocol. The content of the bundle entry may be downloaded or extracted
* to the local file system in order to create a file: URL.
*
* @param url the url
* @return a URL to the content of the bundle entry that uses the file:
* protocol
* @throws Exception if unable to get the file url
*/
public URL getFileURL(URL url) throws Exception;
}

View File

@ -1,54 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* BundleFileLocatorHelperFactory
*
* Obtain a helper for locating files based on the bundle.
*/
public class BundleFileLocatorHelperFactory
{
private static final Logger LOG = LoggerFactory.getLogger(BundleFileLocatorHelperFactory.class);
private static BundleFileLocatorHelperFactory _instance = new BundleFileLocatorHelperFactory();
private BundleFileLocatorHelperFactory()
{
}
public static BundleFileLocatorHelperFactory getFactory()
{
return _instance;
}
public BundleFileLocatorHelper getHelper()
{
BundleFileLocatorHelper helper = BundleFileLocatorHelper.DEFAULT;
try
{
//see if a fragment has supplied an alternative
helper = (BundleFileLocatorHelper)Class.forName(BundleFileLocatorHelper.CLASS_NAME)
.getDeclaredConstructor().newInstance();
}
catch (Throwable t)
{
LOG.trace("IGNORED", t);
}
return helper;
}
}

View File

@ -1,84 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.utils;
import java.util.Dictionary;
import java.util.Hashtable;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import org.osgi.util.tracker.ServiceTracker;
/**
* Utility class for emitting OSGi EventAdmin events
*/
public class EventSender
{
//OSGi Event Admin events for webapps
public static final String DEPLOYING_EVENT = "org/osgi/service/web/DEPLOYING";
public static final String DEPLOYED_EVENT = "org/osgi/service/web/DEPLOYED";
public static final String UNDEPLOYING_EVENT = "org/osgi/service/web/UNDEPLOYING";
public static final String UNDEPLOYED_EVENT = "org/osgi/service/web/UNDEPLOYED";
public static final String FAILED_EVENT = "org/osgi/service/web/FAILED";
private static final EventSender __instance = new EventSender();
private Bundle _myBundle;
private ServiceTracker _serviceTracker;
private EventSender()
{
_myBundle = FrameworkUtil.getBundle(EventSender.class);
_serviceTracker = new ServiceTracker(_myBundle.getBundleContext(), EventAdmin.class.getName(), null);
_serviceTracker.open();
}
public static EventSender getInstance()
{
return __instance;
}
public void send(String topic, Bundle wab, String contextPath)
{
if (topic == null || wab == null || contextPath == null)
return;
send(topic, wab, contextPath, null);
}
public void send(String topic, Bundle wab, String contextPath, Exception ex)
{
EventAdmin service = (EventAdmin)_serviceTracker.getService();
if (service != null)
{
Dictionary<String, Object> props = new Hashtable<>();
props.put("bundle.symbolicName", wab.getSymbolicName());
props.put("bundle.id", wab.getBundleId());
props.put("bundle", wab);
props.put("bundle.version", wab.getVersion());
props.put("context.path", contextPath);
props.put("timestamp", System.currentTimeMillis());
props.put("extender.bundle", _myBundle);
props.put("extender.bundle.symbolicName", _myBundle.getSymbolicName());
props.put("extender.bundle.id", _myBundle.getBundleId());
props.put("extender.bundle.version", _myBundle.getVersion());
if (FAILED_EVENT.equalsIgnoreCase(topic) && ex != null)
props.put("exception", ex);
service.sendEvent(new Event(topic, props));
}
}
}

View File

@ -1,66 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.utils;
import java.net.URL;
import java.net.URLClassLoader;
/**
* FakeURLClassLoader
* <p>
* A URLClassloader that overrides the getURLs() method to return the list
* of urls passed in to the constructor, but otherwise acts as if it has no
* urls, which would cause it to delegate to the parent classloader (in this
* case an OSGi classloader).
* <p>
* The main use of this class is with jars containing tlds. Jasper expects a
* URL classloader to inspect for jars with tlds.
*/
public class FakeURLClassLoader extends URLClassLoader
{
private URL[] _jars;
public FakeURLClassLoader(ClassLoader osgiClassLoader, URL[] jars)
{
super(new URL[]{}, osgiClassLoader);
_jars = jars;
}
/**
* @return the jars that contains tlds so that TldLocationsCache or
* TldScanner can find them.
*/
@Override
public URL[] getURLs()
{
return _jars;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
if (_jars != null)
{
for (URL u : _jars)
{
builder.append(" " + u.toString());
}
return builder.toString();
}
else
return super.toString();
}
}

View File

@ -1,160 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.utils;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import org.osgi.framework.Bundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* OSGiClassLoader
*
* Class loader that is aware of a bundle. Similar to WebAppClassLoader from Jetty
* and the OSGiWebAppClassLoader, but works without webapps.
*/
public class OSGiClassLoader extends URLClassLoader
{
private static final Logger LOG = LoggerFactory.getLogger(OSGiClassLoader.class);
private Bundle _bundle;
private ClassLoader _osgiBundleClassLoader;
private ClassLoader _parent;
public OSGiClassLoader(ClassLoader parent, Bundle bundle)
{
super(new URL[]{}, parent);
_parent = getParent();
_bundle = bundle;
_osgiBundleClassLoader = BundleClassLoaderHelperFactory.getFactory().getHelper().getBundleClassLoader(_bundle);
}
/**
* Get a resource from the classloader
*
* Copied from WebAppClassLoader
*/
@Override
public URL getResource(String name)
{
URL url = null;
boolean triedParent = false;
if (url == null)
{
url = _osgiBundleClassLoader.getResource(name);
if (url == null && name.startsWith("/"))
{
if (LOG.isDebugEnabled())
LOG.debug("HACK leading / off {}", name);
url = _osgiBundleClassLoader.getResource(name.substring(1));
}
}
if (url == null && !triedParent)
{
if (_parent != null)
url = _parent.getResource(name);
}
if (url != null)
if (LOG.isDebugEnabled())
LOG.debug("getResource({})={}", name, url);
return url;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException
{
return loadClass(name, false);
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name))
{
Class<?> c = findLoadedClass(name);
ClassNotFoundException ex = null;
boolean triedParent = false;
if (c == null)
{
try
{
c = this.findClass(name);
}
catch (ClassNotFoundException e)
{
ex = e;
}
}
if (c == null && _parent != null && !triedParent)
c = _parent.loadClass(name);
if (c == null)
throw ex;
if (resolve)
resolveClass(c);
if (LOG.isDebugEnabled())
LOG.debug("loaded {} from {}", c, c.getClassLoader());
return c;
}
}
@Override
public Enumeration<URL> getResources(String name) throws IOException
{
Enumeration<URL> osgiUrls = _osgiBundleClassLoader.getResources(name);
Enumeration<URL> urls = super.getResources(name);
return Collections.enumeration(toList(osgiUrls, urls));
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
return _osgiBundleClassLoader.loadClass(name);
}
/**
*
*/
private List<URL> toList(Enumeration<URL> e, Enumeration<URL> e2)
{
List<URL> list = new ArrayList<>();
while (e != null && e.hasMoreElements())
{
list.add(e.nextElement());
}
while (e2 != null && e2.hasMoreElements())
{
list.add(e2.nextElement());
}
return list;
}
}

View File

@ -1,91 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.utils;
import java.io.FileWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.component.AbstractLifeCycle.AbstractLifeCycleListener;
import org.eclipse.jetty.util.component.LifeCycle;
/**
* ServerConnectorListener
*
* This is for test support, where we need jetty to run on a random port, and we need
* a client to be able to find out which port was picked.
*/
public class ServerConnectorListener extends AbstractLifeCycleListener
{
private Path _filePath;
private String _sysPropertyName;
@Override
public void lifeCycleStarted(LifeCycle event)
{
if (getFilePath() != null)
{
try (FileWriter writer = new FileWriter(getFilePath().toFile()))
{
Files.deleteIfExists(_filePath);
writer.write(((ServerConnector)event).getLocalPort());
writer.close();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
if (getSysPropertyName() != null)
{
System.setProperty(_sysPropertyName, String.valueOf(((ServerConnector)event).getLocalPort()));
}
super.lifeCycleStarted(event);
}
/**
* @return the filePath
*/
public Path getFilePath()
{
return _filePath;
}
/**
* @param filePath the filePath to set
*/
public void setFilePath(Path filePath)
{
_filePath = filePath;
}
/**
* @return the sysPropertyName
*/
public String getSysPropertyName()
{
return _sysPropertyName;
}
/**
* @param sysPropertyName the sysPropertyName to set
*/
public void setSysPropertyName(String sysPropertyName)
{
_sysPropertyName = sysPropertyName;
}
}

View File

@ -1,36 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.utils;
import java.net.URL;
import org.eclipse.jetty.deploy.DeploymentManager;
/**
* TldBundleDiscoverer
*
* Convert bundles that contain tlds into URL locations for consumption by jasper.
*/
public interface TldBundleDiscoverer
{
/**
* Find bundles that contain tlds and convert into URL references to their location.
*
* @param manager The {@link DeploymentManager} instance to use
* @param fileLocator the {@link BundleFileLocatorHelper} instance to use
* @return array of URLs representing locations of tld containing bundles
* @throws Exception In case of errors during resolving TLDs files
*/
URL[] getUrlsForBundlesWithTlds(DeploymentManager manager, BundleFileLocatorHelper fileLocator) throws Exception;
}

View File

@ -1,166 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.utils;
import java.net.URL;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.List;
import java.util.StringTokenizer;
import org.eclipse.jetty.ee10.osgi.boot.OSGiServerConstants;
import org.eclipse.jetty.util.StringUtil;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
/**
* Various useful functions utility methods for OSGi wide use.
*/
public class Util
{
/**
* Create an osgi filter for the given classname and server name.
*
* @param bundleContext the {@link BundleContext} instance to use
* @param classname the class to match on the filter
* @param managedServerName the name of the jetty server instance
* @return a new filter
* @throws InvalidSyntaxException If the filter contains an invalid string that cannot be parsed.
*/
public static Filter createFilter(BundleContext bundleContext, String classname, String managedServerName) throws InvalidSyntaxException
{
if (StringUtil.isBlank(managedServerName) || managedServerName.equals(OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME))
{
return bundleContext.createFilter("(&(objectclass=" + classname + ")(|(managedServerName=" + managedServerName + ")(!(managedServerName=*))))");
}
else
{
return bundleContext.createFilter("(&(objectclass=" + classname + ")(managedServerName=" + managedServerName + "))");
}
}
/**
* Get the value of a manifest header.
*
* @param name the name of the header
* @param altName an alternative name for the header (useful for deprecated names)
* @param manifest the dictionary
* @return the value from the manifest
*/
public static String getManifestHeaderValue(String name, String altName, Dictionary<String, String> manifest)
{
if (manifest == null)
return null;
if (name == null && altName == null)
return null;
if (name != null)
return (String)manifest.get(name);
return (String)manifest.get(altName);
}
/**
* Get the value of a manifest header.
*
* @param name the name of the header
* @param manifest the dictionary
* @return the value from the manifest
*/
public static String getManifestHeaderValue(String name, Dictionary<String, String> manifest)
{
return getManifestHeaderValue(name, null, manifest);
}
/**
* Treating the string as a separated list of filenames,
* convert and return the list of urls.
*
* @param val the separated list of filenames
* @param delims the separators (default is <code>,;</code>)
* @return the list of URLs found in the input list
* @throws Exception if unable to convert entry to a URL
*/
public static List<URL> fileNamesAsURLs(String val, String delims)
throws Exception
{
String separators = StringUtil.DEFAULT_DELIMS;
if (delims == null)
delims = separators;
StringTokenizer tokenizer = new StringTokenizer(val, delims, false);
List<URL> urls = new ArrayList<>();
while (tokenizer.hasMoreTokens())
{
urls.add(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(new URL(tokenizer.nextToken())));
}
return urls;
}
public static void setProperty(Dictionary<String, Object> properties, String key, Object value)
{
if (value != null)
{
properties.put(key, value);
}
}
/**
* recursively substitute the <code>${sysprop}</code> by their actual system property.
* <code>${sysprop,defaultvalue}</code> will use <code>'defaultvalue'</code> as the value if no
* sysprop is defined. Not the most efficient code but we are shooting for
* simplicity and speed of development here.
*
* @param value the input string
* @return the string with replaced properties
*/
public static String resolvePropertyValue(String value)
{
int ind = value.indexOf("${");
if (ind == -1)
{
return value;
}
int ind2 = value.indexOf('}', ind);
if (ind2 == -1)
{
return value;
}
String sysprop = value.substring(ind + 2, ind2);
String defaultValue = null;
int comma = sysprop.indexOf(',');
if (comma != -1 && comma + 1 != sysprop.length())
{
defaultValue = sysprop.substring(comma + 1);
defaultValue = resolvePropertyValue(defaultValue);
sysprop = sysprop.substring(0, comma);
}
else
{
defaultValue = "${" + sysprop + "}";
}
String v = System.getProperty(sysprop);
String reminder = value.length() > ind2 + 1 ? value.substring(ind2 + 1) : "";
reminder = resolvePropertyValue(reminder);
if (v != null)
{
return value.substring(0, ind) + v + reminder;
}
else
{
return value.substring(0, ind) + defaultValue + reminder;
}
}
}

View File

@ -1,428 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.utils.internal;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import org.eclipse.jetty.ee10.osgi.boot.utils.BundleClassLoaderHelper;
import org.osgi.framework.Bundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* DefaultBundleClassLoaderHelper
* <p>
* Default implementation of the BundleClassLoaderHelper. Uses introspection to
* support equinox-3.5 and felix-2.0.0
*/
public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper
{
private static final Logger LOG = LoggerFactory.getLogger(BundleClassLoaderHelper.class);
private static enum OSGiContainerType
{
EquinoxOld, EquinoxLuna, FelixOld, Felix403, Concierge
}
;
private static OSGiContainerType osgiContainer;
private static Class<?> Equinox_BundleHost_Class;
private static Class<?> Equinox_EquinoxBundle_Class;
private static Class<?> Felix_BundleImpl_Class;
private static Class<?> Felix_BundleWiring_Class;
//old equinox
private static Method Equinox_BundleHost_getBundleLoader_method;
private static Method Equinox_BundleLoader_createClassLoader_method;
//new equinox
private static Method Equinox_EquinoxBundle_getModuleClassLoader_Method;
//new felix
private static Method Felix_BundleImpl_Adapt_Method;
//old felix
private static Field Felix_BundleImpl_m_Modules_Field;
private static Field Felix_ModuleImpl_m_ClassLoader_Field;
private static Method Felix_BundleWiring_getClassLoader_Method;
// Concierge
private static Class<?> Concierge_BundleImpl_Class;
private static Class<?> Concierge_BundleWiring_Class;
private static Method Concierge_BundleImpl_Adapt_Method;
private static Method Concierge_BundleWiring_getClassLoader_Method;
private static void checkContainerType(Bundle bundle)
{
if (osgiContainer != null)
return;
try
{
Equinox_BundleHost_Class = bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost");
osgiContainer = OSGiContainerType.EquinoxOld;
return;
}
catch (ClassNotFoundException e)
{
LOG.trace("IGNORED", e);
}
try
{
Equinox_EquinoxBundle_Class = bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.internal.framework.EquinoxBundle");
osgiContainer = OSGiContainerType.EquinoxLuna;
return;
}
catch (ClassNotFoundException e)
{
LOG.trace("IGNORED", e);
}
try
{
//old felix or new felix?
Felix_BundleImpl_Class = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.BundleImpl");
try
{
Felix_BundleImpl_Adapt_Method = Felix_BundleImpl_Class.getDeclaredMethod("adapt", new Class[]{Class.class});
osgiContainer = OSGiContainerType.Felix403;
return;
}
catch (NoSuchMethodException e)
{
osgiContainer = OSGiContainerType.FelixOld;
return;
}
}
catch (ClassNotFoundException e)
{
LOG.trace("IGNORED", e);
}
try
{
Concierge_BundleImpl_Class = bundle.getClass().getClassLoader().loadClass("org.eclipse.concierge.BundleImpl");
osgiContainer = OSGiContainerType.Concierge;
return;
}
catch (ClassNotFoundException e)
{
LOG.trace("IGNORED", e);
}
LOG.warn("Unknown OSGi container type");
return;
}
/**
* Assuming the bundle is started.
*
* @param bundle the bundle
* @return classloader object
*/
@Override
public ClassLoader getBundleClassLoader(Bundle bundle)
{
String bundleActivator = (String)bundle.getHeaders().get("Bundle-Activator");
if (bundleActivator == null)
{
bundleActivator = (String)bundle.getHeaders().get("Jetty-ClassInBundle");
}
if (bundleActivator != null)
{
try
{
return bundle.loadClass(bundleActivator).getClassLoader();
}
catch (ClassNotFoundException e)
{
LOG.warn("Unable to load bundle activator {}", bundleActivator, e);
}
}
// resort to introspection
return getBundleClassLoaderForContainer(bundle);
}
/**
*
*/
private ClassLoader getBundleClassLoaderForContainer(Bundle bundle)
{
checkContainerType(bundle);
if (osgiContainer == null)
{
LOG.warn("No classloader for unknown OSGi container type");
return null;
}
switch (osgiContainer)
{
case EquinoxOld:
case EquinoxLuna:
{
return internalGetEquinoxBundleClassLoader(bundle);
}
case FelixOld:
case Felix403:
{
return internalGetFelixBundleClassLoader(bundle);
}
case Concierge:
{
return internalGetConciergeBundleClassLoader(bundle);
}
default:
{
LOG.warn("No classloader found for bundle {}", bundle.getSymbolicName());
return null;
}
}
}
/**
*
*/
private static ClassLoader internalGetEquinoxBundleClassLoader(Bundle bundle)
{
if (osgiContainer == OSGiContainerType.EquinoxOld)
{
String bundleLoaderName = "org.eclipse.osgi.internal.loader.BundleLoader";
try
{
if (Equinox_BundleHost_getBundleLoader_method == null)
{
Equinox_BundleHost_getBundleLoader_method =
Equinox_BundleHost_Class.getDeclaredMethod("getBundleLoader", new Class[]{});
Equinox_BundleHost_getBundleLoader_method.setAccessible(true);
}
Object bundleLoader = Equinox_BundleHost_getBundleLoader_method.invoke(bundle, new Object[]{});
if (Equinox_BundleLoader_createClassLoader_method == null && bundleLoader != null)
{
Equinox_BundleLoader_createClassLoader_method =
bundleLoader.getClass().getClassLoader().loadClass(bundleLoaderName).getDeclaredMethod("createClassLoader", new Class[]{});
Equinox_BundleLoader_createClassLoader_method.setAccessible(true);
}
return (ClassLoader)Equinox_BundleLoader_createClassLoader_method.invoke(bundleLoader, new Object[]{});
}
catch (Throwable t)
{
LOG.warn("Unable to get equinox bundle classloader", t);
return null;
}
}
if (osgiContainer == OSGiContainerType.EquinoxLuna)
{
try
{
if (Equinox_EquinoxBundle_getModuleClassLoader_Method == null)
Equinox_EquinoxBundle_getModuleClassLoader_Method = Equinox_EquinoxBundle_Class.getDeclaredMethod("getModuleClassLoader", new Class[]{
Boolean.TYPE
});
Equinox_EquinoxBundle_getModuleClassLoader_Method.setAccessible(true);
return (ClassLoader)Equinox_EquinoxBundle_getModuleClassLoader_Method.invoke(bundle, new Object[]{
Boolean.FALSE
});
}
catch (Exception e)
{
LOG.warn("Unable to get equinox luna bundle classloader", e);
return null;
}
}
LOG.warn("No classloader for equinox platform for bundle {}", bundle.getSymbolicName());
return null;
}
/**
*
*/
@SuppressWarnings("unchecked")
private static ClassLoader internalGetFelixBundleClassLoader(Bundle bundle)
{
if (osgiContainer == OSGiContainerType.Felix403)
{
try
{
if (Felix_BundleWiring_Class == null)
Felix_BundleWiring_Class = bundle.getClass().getClassLoader().loadClass("org.osgi.framework.wiring.BundleWiring");
Felix_BundleImpl_Adapt_Method.setAccessible(true);
if (Felix_BundleWiring_getClassLoader_Method == null)
{
Felix_BundleWiring_getClassLoader_Method = Felix_BundleWiring_Class.getDeclaredMethod("getClassLoader");
Felix_BundleWiring_getClassLoader_Method.setAccessible(true);
}
Object wiring = Felix_BundleImpl_Adapt_Method.invoke(bundle, new Object[]{Felix_BundleWiring_Class});
return (ClassLoader)Felix_BundleWiring_getClassLoader_Method.invoke(wiring);
}
catch (Exception e)
{
LOG.warn("Unable to get felix bundle classloader", e);
return null;
}
}
if (osgiContainer == OSGiContainerType.FelixOld)
{
try
{
if (Felix_BundleImpl_m_Modules_Field == null)
{
Felix_BundleImpl_m_Modules_Field = Felix_BundleImpl_Class.getDeclaredField("m_modules");
Felix_BundleImpl_m_Modules_Field.setAccessible(true);
}
// Figure out which version of the modules is exported
Object currentModuleImpl;
try
{
Object[] moduleArray = (Object[])Felix_BundleImpl_m_Modules_Field.get(bundle);
currentModuleImpl = moduleArray[moduleArray.length - 1];
}
catch (Throwable ex)
{
try
{
List<Object> moduleArray = (List<Object>)Felix_BundleImpl_m_Modules_Field.get(bundle);
currentModuleImpl = moduleArray.get(moduleArray.size() - 1);
}
catch (Exception e)
{
LOG.warn("Unable to get field {}", Felix_BundleImpl_m_Modules_Field, e);
return null;
}
}
if (Felix_ModuleImpl_m_ClassLoader_Field == null && currentModuleImpl != null)
{
String felixFrameworkModuleImplClassName = "org.apache.felix.framework.ModuleImpl";
String felixFrameworkModuleImplClassLoaderField = "m_classLoader";
try
{
Felix_ModuleImpl_m_ClassLoader_Field = bundle.getClass().getClassLoader().loadClass(felixFrameworkModuleImplClassName).getDeclaredField(felixFrameworkModuleImplClassLoaderField);
Felix_ModuleImpl_m_ClassLoader_Field.setAccessible(true);
}
catch (Exception e)
{
LOG.warn("Unable to find field {}.{}", felixFrameworkModuleImplClassName, felixFrameworkModuleImplClassLoaderField, e);
return null;
}
}
// first make sure that the classloader is ready:
// the m_classLoader field must be initialized by the
// ModuleImpl.getClassLoader() private method.
ClassLoader cl = null;
try
{
cl = (ClassLoader)Felix_ModuleImpl_m_ClassLoader_Field.get(currentModuleImpl);
if (cl != null)
return cl;
}
catch (Exception e)
{
LOG.warn("Unable to get field {}", Felix_ModuleImpl_m_ClassLoader_Field, e);
return null;
}
// looks like it was not ready:
// the m_classLoader field must be initialized by the
// ModuleImpl.getClassLoader() private method.
// this call will do that.
try
{
bundle.loadClass("java.lang.Object");
cl = (ClassLoader)Felix_ModuleImpl_m_ClassLoader_Field.get(currentModuleImpl);
return cl;
}
catch (Exception e)
{
LOG.warn("Unable to get field {}", Felix_ModuleImpl_m_ClassLoader_Field, e);
return null;
}
}
catch (Exception e)
{
LOG.warn("Unable to load old felix container", e);
return null;
}
}
LOG.warn("No classloader for felix platform for bundle {}", bundle.getSymbolicName());
return null;
}
/**
*
*/
private static ClassLoader internalGetConciergeBundleClassLoader(Bundle bundle)
{
if (osgiContainer == OSGiContainerType.Concierge)
{
try
{
/**
* In Concierge:
*
* Option A:
* <pre>
* Concierge concierge = new Concierge(...);
* BundleWiring bundleWiring = concierge.getWiring(); // method is public
* </pre>
* Problem: getWiring not yet implementd
*
* Option B:
* <pre>
* Concierge concierge = new Concierge(...);
* BundleWiring bundleWiring = concierge.adapt(org.osgi.framework.wiring.BundleWiring);
* </pre>
* Same approach as done in Felix.
*
*/
if (Concierge_BundleWiring_Class == null)
{
Concierge_BundleWiring_Class = bundle.getClass().getClassLoader().loadClass("org.osgi.framework.wiring.BundleWiring");
Concierge_BundleImpl_Adapt_Method = Concierge_BundleImpl_Class.getMethod("adapt", new Class[]{Class.class});
Concierge_BundleImpl_Adapt_Method.setAccessible(true);
Concierge_BundleWiring_getClassLoader_Method = Concierge_BundleWiring_Class.getMethod("getClassLoader");
Concierge_BundleWiring_getClassLoader_Method.setAccessible(true);
}
Object wiring = Concierge_BundleImpl_Adapt_Method.invoke(bundle, new Object[]{Concierge_BundleWiring_Class});
ClassLoader cl = (ClassLoader)Concierge_BundleWiring_getClassLoader_Method.invoke(wiring);
return cl;
}
catch (Exception e)
{
LOG.warn("Unable to load Concierge platform", e);
return null;
}
}
LOG.warn("No classloader for Concierge platform for bundle {}", bundle.getSymbolicName());
return null;
}
}

View File

@ -1,385 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee10.osgi.boot.utils.internal;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.zip.ZipFile;
import org.eclipse.jetty.ee10.osgi.boot.utils.BundleFileLocatorHelper;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.util.resource.Resource;
import org.osgi.framework.Bundle;
/**
* DefaultFileLocatorHelper
* <p>
* From a bundle to its location on the filesystem. Assumes the bundle is not a
* jar.
*/
public class DefaultFileLocatorHelper implements BundleFileLocatorHelper
{
// hack to locate the file-system directly from the bundle.
// support equinox, felix and nuxeo's osgi implementations.
// not tested on nuxeo and felix just yet.
// The url nuxeo and felix return is created directly from the File so it
// should work.
private static Field BUNDLE_ENTRY_FIELD = null;
private static Field FILE_FIELD = null;
private static Field BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY = null; // ZipBundleFile
// inside
// DirZipBundleEntry
private static Field ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE = null; // ZipFile
private static final String[] FILE_BUNDLE_ENTRY_CLASSES = {
"org.eclipse.osgi.baseadaptor.bundlefile.FileBundleEntry", "org.eclipse.osgi.storage.bundlefile.FileBundleEntry"
};
private static final String[] ZIP_BUNDLE_ENTRY_CLASSES = {
"org.eclipse.osgi.baseadaptor.bundlefile.ZipBundleEntry", "org.eclipse.osgi.storage.bundlefile.ZipBundleEntry"
};
private static final String[] DIR_ZIP_BUNDLE_ENTRY_CLASSES = {
"org.eclipse.osgi.baseadaptor.bundlefile.DirZipBundleEntry", "org.eclipse.osgi.storage.bundlefile.DirZipBundleEntry"
};
private static final String[] BUNDLE_URL_CONNECTION_CLASSES = {
"org.eclipse.osgi.framework.internal.core.BundleURLConnection", "org.eclipse.osgi.storage.url.BundleURLConnection"
};
public static boolean match(String name, String... names)
{
if (name == null || names == null)
return false;
boolean matched = false;
for (int i = 0; i < names.length && !matched; i++)
{
if (name.equals(names[i]))
matched = true;
}
return matched;
}
/**
* Works with equinox, felix, nuxeo and probably more. Not exactly in the
* spirit of OSGi but quite necessary to support self-contained webapps and
* other situations.
*
* @param bundle The bundle
* @return Its installation location as a file.
* @throws Exception if unable to get the bundle install location
*/
@SuppressWarnings("resource")
public File getBundleInstallLocation(Bundle bundle) throws Exception
{
// String installedBundles = System.getProperty("osgi.bundles");
// grab the MANIFEST.MF's url
// and then do what it takes.
URL url = bundle.getEntry("/META-INF/MANIFEST.MF");
if (url.getProtocol().equals("file"))
{
// some osgi frameworks do use the file protocol directly in some
// situations. Do use the PathResource to transform the URL into a
// File: URL#toURI is broken
return new PathResource(url).getFile().getParentFile().getParentFile().getCanonicalFile();
}
else if (url.getProtocol().equals("bundleentry"))
{
// say hello to equinox who has its own protocol.
// we use introspection like there is no tomorrow to get access to
// the File
URLConnection con = url.openConnection();
con.setUseCaches(Resource.getDefaultUseCaches()); // work around
// problems where
// url connections
// cache
// references to
// jars
if (BUNDLE_ENTRY_FIELD == null)
{
BUNDLE_ENTRY_FIELD = con.getClass().getDeclaredField("bundleEntry");
BUNDLE_ENTRY_FIELD.setAccessible(true);
}
Object bundleEntry = BUNDLE_ENTRY_FIELD.get(con);
if (match(bundleEntry.getClass().getName(), FILE_BUNDLE_ENTRY_CLASSES))
{
if (FILE_FIELD == null)
{
FILE_FIELD = bundleEntry.getClass().getDeclaredField("file");
FILE_FIELD.setAccessible(true);
}
File f = (File)FILE_FIELD.get(bundleEntry);
return f.getParentFile().getParentFile().getCanonicalFile();
}
else if (match(bundleEntry.getClass().getName(), ZIP_BUNDLE_ENTRY_CLASSES))
{
url = bundle.getEntry("/");
con = url.openConnection();
con.setDefaultUseCaches(Resource.getDefaultUseCaches());
if (BUNDLE_ENTRY_FIELD == null)
{
// this one will be a DirZipBundleEntry
BUNDLE_ENTRY_FIELD = con.getClass().getDeclaredField("bundleEntry");
BUNDLE_ENTRY_FIELD.setAccessible(true);
}
bundleEntry = BUNDLE_ENTRY_FIELD.get(con);
if (BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY == null)
{
BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY = bundleEntry.getClass().getDeclaredField("bundleFile");
BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY.setAccessible(true);
}
Object zipBundleFile = BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY.get(bundleEntry);
if (ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE == null)
{
ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE = zipBundleFile.getClass().getDeclaredField("zipFile");
ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE.setAccessible(true);
}
ZipFile zipFile = (ZipFile)ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE.get(zipBundleFile);
return new File(zipFile.getName());
}
else if (match(bundleEntry.getClass().getName(), DIR_ZIP_BUNDLE_ENTRY_CLASSES))
{
// that will not happen as we did ask for the manifest not a
// directory.
}
}
else if ("bundle".equals(url.getProtocol()))
{
// observed this on felix-2.0.0
String location = bundle.getLocation();
if (location.startsWith("file:/"))
{
URI uri = new URI(URIUtil.encodePath(location));
return new File(uri).getCanonicalFile();
}
else if (location.startsWith("file:"))
{
// location defined in the BundleArchive m_bundleArchive
// it is relative to relative to the BundleArchive's
// m_archiveRootDir
File res = new File(location.substring("file:".length()));
if (!res.exists())
{
return null;
// Object bundleArchive = getFelixBundleArchive(bundle);
// File archiveRoot =
// getFelixBundleArchiveRootDir(bundleArchive);
// String currentLocation =
// getFelixBundleArchiveCurrentLocation(bundleArchive);
// System.err.println("Got the archive root " +
// archiveRoot.getAbsolutePath()
// + " current location " + currentLocation +
// " is directory ?");
// res = new File(archiveRoot, currentLocation != null
// ? currentLocation : location.substring("file:".length()));
}
return res;
}
else if (location.startsWith("reference:file:"))
{
location = URLDecoder.decode(location.substring("reference:".length()), "UTF-8");
File file = new File(location.substring("file:".length())).getCanonicalFile();
return file;
}
}
return null;
}
/**
* Locate a file inside a bundle.
*
* @param bundle the bundle
* @param path the path
* @return file object
* @throws Exception if unable to get the file in the bundle
*/
@Override
public File getFileInBundle(Bundle bundle, String path) throws Exception
{
if (path != null && path.length() > 0 && path.charAt(0) == '/')
{
path = path.substring(1);
}
File bundleInstall = getBundleInstallLocation(bundle);
File webapp = path != null && path.length() != 0 ? new File(bundleInstall, path) : bundleInstall;
if (!webapp.exists())
{
throw new IllegalArgumentException("Unable to locate " + path + " inside " + bundle.getSymbolicName() +
" (" + (bundleInstall != null ? bundleInstall.getAbsolutePath() : " no_bundle_location ") + ")");
}
return webapp;
}
/**
* Helper method equivalent to Bundle#getEntry(String entryPath) except that
* it searches for entries in the fragments by using the Bundle#findEntries
* method.
*
* @param bundle the bundle
* @param entryPath the entry path
* @return null or all the entries found for that path.
*/
@Override
public Enumeration<URL> findEntries(Bundle bundle, String entryPath)
{
int last = entryPath.lastIndexOf('/');
String path = last != -1 && last < entryPath.length() - 2 ? entryPath.substring(0, last) : "/";
if (!path.startsWith("/"))
{
path = "/" + path;
}
String pattern = last != -1 && last < entryPath.length() - 2 ? entryPath.substring(last + 1) : entryPath;
@SuppressWarnings("unchecked")
Enumeration<URL> enUrls = bundle.findEntries(path, pattern, false);
return enUrls;
}
/**
* If the bundle is a jar, returns the jar. If the bundle is a folder, look
* inside it and search for jars that it returns.
* <p>
* Good enough for our purpose (TldLocationsCache when it scans for tld
* files inside jars alone. In fact we only support the second situation for
* development purpose where the bundle was imported in pde and the classes
* kept in a jar.
* </p>
*
* @param bundle the bundle
* @return The jar(s) file that is either the bundle itself, either the jars
* embedded inside it.
*/
@Override
public File[] locateJarsInsideBundle(Bundle bundle) throws Exception
{
File jasperLocation = getBundleInstallLocation(bundle);
if (jasperLocation.isDirectory())
{
// try to find the jar files inside this folder
ArrayList<File> urls = new ArrayList<>();
for (File f : jasperLocation.listFiles())
{
if (FileID.isJavaArchive(f.getName()) && f.isFile())
{
urls.add(f);
}
else if (f.isDirectory() && f.getName().equals("lib"))
{
for (File f2 : jasperLocation.listFiles())
{
if (FileID.isJavaArchive(f2.getName()) && f2.isFile())
{
urls.add(f2);
}
}
}
}
return urls.toArray(new File[urls.size()]);
}
else
{
return new File[]{jasperLocation};
}
}
// introspection on equinox to invoke the getLocalURL method on
// BundleURLConnection
// equivalent to using the FileLocator without depending on an equinox
// class.
private static Method BUNDLE_URL_CONNECTION_getLocalURL = null;
private static Method BUNDLE_URL_CONNECTION_getFileURL = null;
/**
* Only useful for equinox: on felix we get the file:// or jar:// url
* already. Other OSGi implementations have not been tested
* <p>
* Get a URL to the bundle entry that uses a common protocol (i.e. file:
* jar: or http: etc.).
* </p>
*
* @return a URL to the bundle entry that uses a common protocol
*/
@Override
public URL getLocalURL(URL url)
throws Exception
{
if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol()))
{
URLConnection conn = url.openConnection();
conn.setDefaultUseCaches(Resource.getDefaultUseCaches());
if (BUNDLE_URL_CONNECTION_getLocalURL == null && match(conn.getClass().getName(), BUNDLE_URL_CONNECTION_CLASSES))
{
BUNDLE_URL_CONNECTION_getLocalURL = conn.getClass().getMethod("getLocalURL");
BUNDLE_URL_CONNECTION_getLocalURL.setAccessible(true);
}
if (BUNDLE_URL_CONNECTION_getLocalURL != null)
{
return (URL)BUNDLE_URL_CONNECTION_getLocalURL.invoke(conn);
}
}
return url;
}
/**
* Only useful for equinox: on felix we get the file:// url already. Other
* OSGi implementations have not been tested
* <p>
* Get a URL to the content of the bundle entry that uses the file:
* protocol. The content of the bundle entry may be downloaded or extracted
* to the local file system in order to create a file: URL.
* </p>
*
* @return a URL to the content of the bundle entry that uses the file:
* protocol
* @throws Exception if unable to get the file url
*/
@Override
public URL getFileURL(URL url) throws Exception
{
if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol()))
{
URLConnection conn = url.openConnection();
conn.setDefaultUseCaches(Resource.getDefaultUseCaches());
if (BUNDLE_URL_CONNECTION_getFileURL == null && match(conn.getClass().getName(), BUNDLE_URL_CONNECTION_CLASSES))
{
BUNDLE_URL_CONNECTION_getFileURL = conn.getClass().getMethod("getFileURL");
BUNDLE_URL_CONNECTION_getFileURL.setAccessible(true);
}
if (BUNDLE_URL_CONNECTION_getFileURL != null)
{
return (URL)BUNDLE_URL_CONNECTION_getFileURL.invoke(conn);
}
}
return url;
}
}

View File

@ -1,2 +1,2 @@
org.eclipse.jetty.ee10.osgi.annotations.AnnotationConfiguration
org.eclipse.jetty.ee10.osgi.boot.OSGiWebInfConfiguration
org.eclipse.jetty.ee10.osgi.boot.OSGiMetaInfConfiguration

View File

@ -12,23 +12,30 @@
<packaging>pom</packaging>
<properties>
<osgi-version>3.17.200</osgi-version>
<osgi-services-version>3.10.200</osgi-services-version>
<osgi-util-version>3.6.100</osgi-util-version>
<osgi-version>3.18.100</osgi-version>
<osgi-annotation-version>8.1.0</osgi-annotation-version>
<osgi-services-version>3.11.0</osgi-services-version>
<osgi-service-cm-version>1.6.1</osgi-service-cm-version>
<osgi-service-component-version>1.5.0</osgi-service-component-version>
<osgi-service-event-version>1.4.1</osgi-service-event-version>
<osgi-util-version>3.7.100</osgi-util-version>
<osgi-util-function-version>1.2.0</osgi-util-function-version>
<osgi-util-promise-version>1.2.0</osgi-util-promise-version>
<osgi-util-measurement-version>1.0.2</osgi-util-measurement-version>
<osgi-util-position-version>1.0.1</osgi-util-position-version>
<osgi-util-tracker-version>1.5.4</osgi-util-tracker-version>
<osgi-util-xml-version>1.0.2</osgi-util-xml-version>
<equinox-http-servlet-version>1.0.0-v20070606</equinox-http-servlet-version>
<jacoco.skip>true</jacoco.skip>
</properties>
<modules>
<module>jetty-ee10-osgi-alpn</module>
<module>jetty-ee10-osgi-boot</module>
<module>jetty-ee10-osgi-boot-jsp</module>
<module>jetty-ee10-osgi-boot-warurl</module>
<module>test-jetty-ee10-osgi-webapp</module>
<module>test-jetty-ee10-osgi-webapp-resources</module>
<module>test-jetty-ee10-osgi-context</module>
<module>test-jetty-ee10-osgi-fragment</module>
<module>test-jetty-ee10-osgi-server</module>
<module>jetty-ee10-osgi-alpn</module>
<module>test-jetty-ee10-osgi</module>
</modules>
@ -60,24 +67,6 @@
</resource>
</resources>
<plugins>
<!-- necessary because x.y.x-SNAPSHOT is not a valid osgi version -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>set-servlet-api-version</id>
<phase>validate</phase>
<goals>
<goal>parse-version</goal>
</goals>
<configuration>
<versionString>${jetty.servlet.api.version}</versionString>
<propertyPrefix>servletImpl</propertyPrefix>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
@ -117,11 +106,36 @@
<artifactId>org.eclipse.osgi</artifactId>
<version>${osgi-version}</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.cm</artifactId>
<version>${osgi-service-cm-version}</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.component</artifactId>
<version>${osgi-service-component-version}</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.event</artifactId>
<version>${osgi-service-event-version}</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.util.tracker</artifactId>
<version>${osgi-util-tracker-version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.equinox.http</groupId>
<artifactId>servlet</artifactId>
<version>${equinox-http-servlet-version}</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi.annotation</artifactId>
<version>${osgi-annotation-version}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@ -1,79 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.ee10.osgi</groupId>
<artifactId>jetty-ee10-osgi-project</artifactId>
<version>12.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-jetty-ee10-osgi-context</artifactId>
<name>EE10 :: Jetty :: OSGi :: Test Context</name>
<description>Test Jetty OSGi bundle with a ContextHandler</description>
<properties>
<bundle-symbolic-name>${project.groupId}.testcontext</bundle-symbolic-name>
<maven.deploy.skip>true</maven.deploy.skip>
<maven.javadoc.skip>true</maven.javadoc.skip>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.osgi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/context</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>org.eclipse.jetty.ee10.osgi.testcontext;singleton:=true</Bundle-SymbolicName>
<Bundle-Name>Jetty OSGi Test Context</Bundle-Name>
<Bundle-Activator>com.acme.osgi.Activator</Bundle-Activator>
<Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment>
<!-- disable the uses directive: jetty will accommodate pretty much any versions
of the packages it uses; no need to reflect some tight dependency determined at
compilation time. -->
<_nouses>true</_nouses>
<Import-Package>
jakarta.servlet;version="[$(version;==;${servletImpl.osgiVersion}),$(version;+;${servletImpl.osgiVersion}))",
jakarta.servlet.resources;version="[$(version;==;${servletImpl.osgiVersion}),$(version;+;${servletImpl.osgiVersion}))",
org.osgi.framework,
org.osgi.service.cm;version="1.2.0",
org.osgi.service.packageadmin,
org.osgi.service.startlevel;version="1.0.0",
org.osgi.service.url;version="1.0.0",
org.osgi.util.tracker;version="1.3.0",
*
</Import-Package>
<DynamicImport-Package>org.eclipse.jetty.*;version="[$(version;==;${parsedVersion.osgiVersion}),$(version;+;${parsedVersion.osgiVersion}))"</DynamicImport-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
<Set name="contextPath">/unset</Set>
<!-- Set up the base resource for static files relative to inside bundle -->
<Set name="baseResourceAsString"><Property name="bundle.root"/>/static/</Set>
<Set name="handler">
<New class="org.eclipse.jetty.server.handler.ResourceHandler">
<Set name="welcomeFiles">
<Array type="String">
<Item>index.html</Item>
</Array>
</Set>
<Set name="cacheControl">max-age=3600,public</Set>
</New>
</Set>
</Configure>

View File

@ -1,72 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package com.acme.osgi;
import java.util.Dictionary;
import java.util.Hashtable;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
/**
* Bootstrap a ContextHandler
*/
public class Activator implements BundleActivator
{
private ServiceRegistration _sr;
/**
*
*/
@Override
public void start(final BundleContext context) throws Exception
{
ContextHandler ch = new ContextHandler();
ch.addEventListener(new ServletContextListener()
{
@Override
public void contextInitialized(ServletContextEvent sce)
{
//System.err.println("Context is initialized");
}
@Override
public void contextDestroyed(ServletContextEvent sce)
{
//System.err.println("CONTEXT IS DESTROYED!");
}
});
Dictionary props = new Hashtable();
props.put("Web-ContextPath", "/acme");
props.put("Jetty-ContextFilePath", "acme.xml");
_sr = context.registerService(ContextHandler.class.getName(), ch, props);
}
/**
* Stop the activator.
*
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
@Override
public void stop(BundleContext context) throws Exception
{
_sr.unregister();
}
}

View File

@ -1,6 +0,0 @@
<html>
<body>
<h1>Test OSGi Context</h1>
<p>ContextHandler registered as a service successfully deployed.</p>
</body>
</html>

View File

@ -57,7 +57,7 @@
org.xml.sax.helpers,
*
</Import-Package>
<DynamicImport-Package>org.eclipse.jetty.*;version="[$(version;==;${parsedVersion.osgiVersion}),$(version;+;${parsedVersion.osgiVersion}))"</DynamicImport-Package>
<DynamicImport-Package>org.eclipse.jetty.*;version="[$(version;==;${parsedVersion.osgiVersion}),$(version;+;${parsedVersion.osgiVersion}))", org.eclipse.jetty.ee10.*;version="[$(version;==;${parsedVersion.osgiVersion}),$(version;+;${parsedVersion.osgiVersion}))"</DynamicImport-Package>
</instructions>
</configuration>
</plugin>

View File

@ -47,7 +47,8 @@
</supportedProjectTypes>
<instructions>
<Export-Package>!com.acme*</Export-Package>
<Web-ContextPath>/</Web-ContextPath>
<Web-ContextPath>/test-webapp-resources</Web-ContextPath>
<Jetty-Environment>ee10</Jetty-Environment>
</instructions>
</configuration>
</plugin>

View File

@ -1,61 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.ee10.osgi</groupId>
<artifactId>jetty-ee10-osgi-project</artifactId>
<version>12.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-jetty-ee10-osgi-webapp</artifactId>
<name>EE10 :: Jetty :: OSGi :: Test WebApp</name>
<description>Test Jetty OSGi Webapp bundle</description>
<properties>
<bundle-symbolic-name>${project.groupId}.webapp</bundle-symbolic-name>
<maven.deploy.skip>true</maven.deploy.skip>
<maven.javadoc.skip>true</maven.javadoc.skip>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<artifactId>jetty-ee10-webapp</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.osgi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.platform</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>org.eclipse.jetty.ee10.osgi.testapp;singleton:=true</Bundle-SymbolicName>
<Bundle-Name>Jetty OSGi Test WebApp</Bundle-Name>
<Bundle-Activator>com.acme.osgi.Activator</Bundle-Activator>
<Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment>
<!-- disable the uses directive: jetty will accommodate pretty much any versions
of the packages it uses; no need to reflect some tight dependency determined at
compilation time. -->
<Export-Package>com.acme.osgi</Export-Package>
<DynamicImport-Package>org.eclipse.jetty.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))"</DynamicImport-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,91 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package com.acme.osgi;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Dictionary;
import java.util.Hashtable;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
/**
* Bootstrap a webapp
*/
public class Activator implements BundleActivator
{
private ServiceRegistration _srA;
private ServiceRegistration _srB;
public static class TestServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
//report the mimetype of a file
String mimetype = req.getServletContext().getMimeType("file.gz");
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.write("<html><body><p>MIMETYPE=" + mimetype + "</p></body</html>");
writer.flush();
}
}
/**
*
*/
@Override
public void start(BundleContext context) throws Exception
{
//Create webappA as a Service and target it at the default server
WebAppContext webapp = new WebAppContext();
webapp.addServlet(new ServletHolder(new TestServlet()), "/mime");
Dictionary props = new Hashtable();
props.put("Jetty-WarResourcePath", "webappA");
props.put("Web-ContextPath", "/acme");
props.put("managedServerName", "defaultJettyServer");
_srA = context.registerService(WebAppContext.class.getName(), webapp, props);
//Create a second webappB as a Service and target it at a custom Server
//deployed by another bundle
final WebAppContext webappB = new WebAppContext();
Dictionary propsB = new Hashtable();
propsB.put("Jetty-WarResourcePath", "webappB");
propsB.put("Web-ContextPath", "/acme");
propsB.put("managedServerName", "fooServer");
_srB = context.registerService(WebAppContext.class.getName(), webappB, propsB);
}
/**
* Stop the activator.
*
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
@Override
public void stop(BundleContext context) throws Exception
{
_srA.unregister();
_srB.unregister();
}
}

View File

@ -1,6 +0,0 @@
<html>
<body>
<h1>Test OSGi WebApp</h1>
<p>Webapp registered by bundle as service successfully deployed.</p>
</body>
</html>

View File

@ -1,6 +0,0 @@
<html>
<body>
<h1>Test OSGi WebAppA</h1>
<p>Webapp registered by bundle as service successfully deployed.</p>
</body>
</html>

View File

@ -1,6 +0,0 @@
<html>
<body>
<h1>Test OSGi WebAppB</h1>
<p>Webapp registered by bundle as service successfully deployed.</p>
</body>
</html>

View File

@ -17,9 +17,8 @@
<pax.url.version>2.6.2</pax.url.version>
<swissbox.version>1.8.3</swissbox.version>
<tinybundles.version>3.0.0</tinybundles.version>
<spifly.version>1.3.4</spifly.version>
<!-- TODO -->
<skipTests>true</skipTests>
<spifly.version>1.3.6</spifly.version>
<injection.bundle.version>1.2</injection.bundle.version>
<maven.javadoc.skip>true</maven.javadoc.skip>
</properties>
<dependencies>
@ -138,17 +137,43 @@
<version>1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.util.promise</artifactId>
<version>${osgi-util-promise-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.util.measurement</artifactId>
<version>${osgi-util-measurement-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.util.position</artifactId>
<version>${osgi-util-position-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.util.xml</artifactId>
<version>${osgi-util-xml-version}</version>
<scope>test</scope>
</dependency>
<!-- Jetty OSGi Deps -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>test</scope>
</dependency>
<!--
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
-->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
@ -255,6 +280,11 @@
<artifactId>jakarta.servlet.jsp.jstl</artifactId>
</dependency>
<!-- Jetty Deps -->
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<artifactId>jetty-ee10-jndi</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<artifactId>jetty-ee10-annotations</artifactId>
@ -306,32 +336,32 @@
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<groupId>org.eclipse.jetty.ee10.websocket</groupId>
<artifactId>jetty-ee10-websocket-jetty-api</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<groupId>org.eclipse.jetty.ee10.websocket</groupId>
<artifactId>jetty-ee10-websocket-jetty-common</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<groupId>org.eclipse.jetty.ee10.websocket</groupId>
<artifactId>jetty-ee10-websocket-jetty-client</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<groupId>org.eclipse.jetty.ee10.websocket</groupId>
<artifactId>jetty-ee10-websocket-jakarta-client</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<groupId>org.eclipse.jetty.ee10.websocket</groupId>
<artifactId>jetty-ee10-websocket-servlet</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<groupId>org.eclipse.jetty.ee10.websocket</groupId>
<artifactId>jetty-ee10-websocket-jetty-server</artifactId>
<scope>runtime</scope>
</dependency>
@ -341,7 +371,7 @@
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<groupId>org.eclipse.jetty.ee10.websocket</groupId>
<artifactId>jetty-ee10-websocket-jakarta-server</artifactId>
<scope>runtime</scope>
</dependency>
@ -353,12 +383,6 @@
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>jetty-http2-hpack</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10.osgi</groupId>
<artifactId>jetty-ee10-osgi-alpn</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-alpn-server</artifactId>
@ -370,32 +394,38 @@
<artifactId>jetty-ee10-plus</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10.osgi</groupId>
<artifactId>jetty-ee10-osgi-alpn</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<!-- Eclipse OSGi Deps -->
<dependency>
<groupId>org.eclipse.jetty.ee10.demos</groupId>
<artifactId>demo-jsp-webapp</artifactId>
<artifactId>jetty-ee10-demo-jsp-webapp</artifactId>
<version>${project.version}</version>
<classifier>webbundle</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10.demos</groupId>
<artifactId>demo-jetty-webapp</artifactId>
<artifactId>jetty-ee10-demo-jetty-webapp</artifactId>
<version>${project.version}</version>
<classifier>webbundle</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10.demos</groupId>
<artifactId>demo-spec-webapp</artifactId>
<artifactId>jetty-ee10-demo-spec-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10.demos</groupId>
<artifactId>demo-container-initializer</artifactId>
<artifactId>jetty-ee10-demo-container-initializer</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
@ -420,21 +450,9 @@
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10.demos</groupId>
<artifactId>demo-mock-resources</artifactId>
<artifactId>jetty-ee10-demo-mock-resources</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10.osgi</groupId>
<artifactId>test-jetty-ee10-osgi-context</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10.osgi</groupId>
<artifactId>test-jetty-ee10-osgi-webapp</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
@ -525,6 +543,12 @@
<settingsFilePath>${env.GLOBAL_MVN_SETTINGS}</settingsFilePath>
</systemPropertyVariables>
<argLine>-Dconscrypt-version=${conscrypt.version}</argLine>
<!-- TODO -->
<excludes>
<exclude>**/TestJettyOSGiBootHTTP2Conscrypt.java</exclude>
<exclude>**/TestJettyOSGiBootHTTP2JDK9.java</exclude>
<exclude>**/TestJettyOSGiBootWithJakartaWebSocket.java</exclude>
</excludes>
</configuration>
<!-- paxexam still using junit 4 so we have to force the provider here -->
<dependencies>

View File

@ -12,10 +12,6 @@
<Set name="contexts">
<Ref refid="Contexts" />
</Set>
<Call name="setContextAttribute">
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
<Arg>.*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$</Arg>
</Call>
</New>
</Arg>
</Call>

View File

@ -1,48 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<!-- ============================================================= -->
<!-- Configure the Jetty Server instance with an ID "Server" -->
<!-- by adding an HTTP connector. -->
<!-- This configuration must be used in conjunction with jetty.xml -->
<!-- ============================================================= -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- =========================================================== -->
<!-- Add an HTTP Connector. -->
<!-- Configure an o.e.j.server.ServerConnector with a single -->
<!-- HttpConnectionFactory instance using the common httpConfig -->
<!-- instance defined in jetty.xml -->
<!-- -->
<!-- Consult the javadoc of o.e.j.server.ServerConnector and -->
<!-- o.e.j.server.HttpConnectionFactory for all configuration -->
<!-- that may be set here. -->
<!-- =========================================================== -->
<Call name="addConnector">
<Arg>
<New class="org.eclipse.jetty.server.ServerConnector">
<Arg name="server"><Ref refid="Server" /></Arg>
<Arg name="factories">
<Array type="org.eclipse.jetty.server.ConnectionFactory">
<Item>
<New class="org.eclipse.jetty.server.HttpConnectionFactory">
<Arg name="config"><Ref refid="httpConfig" /></Arg>
</New>
</Item>
</Array>
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.osgi.boot.utils.ServerConnectorListener">
<Set name="sysPropertyName">boot.context.service.port</Set>
</New>
</Arg>
</Call>
<Set name="host" property="jetty.http.host"/>
<Set name="port"><Property name="jetty.http.port" default="80"/></Set>
<Set name="idleTimeout"><Property name="jetty.http.idleTimeout" default="30000"/></Set>
</New>
</Arg>
</Call>
</Configure>

View File

@ -1,48 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<!-- ============================================================= -->
<!-- Configure the Jetty Server instance with an ID "Server" -->
<!-- by adding an HTTP connector. -->
<!-- This configuration must be used in conjunction with jetty.xml -->
<!-- ============================================================= -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- =========================================================== -->
<!-- Add an HTTP Connector. -->
<!-- Configure an o.e.j.server.ServerConnector with a single -->
<!-- HttpConnectionFactory instance using the common httpConfig -->
<!-- instance defined in jetty.xml -->
<!-- -->
<!-- Consult the javadoc of o.e.j.server.ServerConnector and -->
<!-- o.e.j.server.HttpConnectionFactory for all configuration -->
<!-- that may be set here. -->
<!-- =========================================================== -->
<Call name="addConnector">
<Arg>
<New class="org.eclipse.jetty.server.ServerConnector">
<Arg name="server"><Ref refid="Server" /></Arg>
<Arg name="factories">
<Array type="org.eclipse.jetty.server.ConnectionFactory">
<Item>
<New class="org.eclipse.jetty.server.HttpConnectionFactory">
<Arg name="config"><Ref refid="httpConfig" /></Arg>
</New>
</Item>
</Array>
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.osgi.boot.utils.ServerConnectorListener">
<Set name="sysPropertyName">boot.webapp.service.port</Set>
</New>
</Arg>
</Call>
<Set name="host" property="jetty.http.host"/>
<Set name="port"><Property name="jetty.http.port" default="80"/></Set>
<Set name="idleTimeout"><Property name="jetty.http.idleTimeout" default="30000"/></Set>
</New>
</Arg>
</Call>
</Configure>

View File

@ -33,7 +33,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.osgi.boot.utils.ServerConnectorListener">
<New class="org.eclipse.jetty.osgi.util.ServerConnectorListener">
<Set name="sysPropertyName">boot.annotations.port</Set>
</New>
</Arg>

View File

@ -33,7 +33,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.osgi.boot.utils.ServerConnectorListener">
<New class="org.eclipse.jetty.osgi.util.ServerConnectorListener">
<Set name="sysPropertyName">boot.bundle.port</Set>
</New>
</Arg>

View File

@ -33,7 +33,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.osgi.boot.utils.ServerConnectorListener">
<New class="org.eclipse.jetty.osgi.util.ServerConnectorListener">
<Set name="sysPropertyName">boot.jakarta.websocket.port</Set>
</New>
</Arg>

View File

@ -33,7 +33,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.osgi.boot.utils.ServerConnectorListener">
<New class="org.eclipse.jetty.osgi.util.ServerConnectorListener">
<Set name="sysPropertyName">boot.jsp.port</Set>
</New>
</Arg>

View File

@ -32,7 +32,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.osgi.boot.utils.ServerConnectorListener">
<New class="org.eclipse.jetty.osgi.util.ServerConnectorListener">
<Set name="sysPropertyName">boot.resources.port</Set>
</New>
</Arg>

View File

@ -33,7 +33,7 @@
</Arg>
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.ee10.osgi.boot.utils.ServerConnectorListener">
<New class="org.eclipse.jetty.osgi.util.ServerConnectorListener">
<Set name="sysPropertyName">boot.websocket.port</Set>
</New>
</Arg>

Some files were not shown because too many files have changed in this diff Show More