blob: 0b7e97df5ece867c08e6f33bd740d2cba8b5a374 [file] [log] [blame]
/*
* Copyright (c) 2007, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package com.sun.enterprise.v3.server;
import org.glassfish.internal.api.ClassLoaderHierarchy;
import jakarta.inject.Inject;
import org.jvnet.hk2.annotations.Optional;
import org.jvnet.hk2.annotations.Service;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.utilities.BuilderHelper;
import org.jvnet.hk2.config.TranslationException;
import org.jvnet.hk2.config.VariableResolver;
import org.glassfish.internal.api.DelegatingClassLoader;
import org.glassfish.internal.api.ConnectorClassLoaderService;
import org.glassfish.api.deployment.archive.ReadableArchive;
import org.glassfish.api.deployment.DeploymentContext;
import com.sun.enterprise.module.HK2Module;
import java.net.URI;
import java.net.MalformedURLException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.jar.Manifest;
import java.io.File;
import java.io.IOException;
import com.sun.enterprise.module.*;
import com.sun.enterprise.module.common_impl.DirectoryBasedRepository;
import com.sun.enterprise.module.common_impl.Tokenizer;
/**
* @author Sanjeeb.Sahoo@Sun.COM
*/
@Service
public class ClassLoaderHierarchyImpl implements ClassLoaderHierarchy {
@Inject APIClassLoaderServiceImpl apiCLS;
@Inject CommonClassLoaderServiceImpl commonCLS;
//For distributions where connector module is not available.
@Inject @Optional ConnectorClassLoaderService connectorCLS;
@Inject
AppLibClassLoaderServiceImpl applibCLS;
@Inject
ModulesRegistry modulesRegistry;
@Inject
Logger logger;
@Inject
ServiceLocator habitat;
SystemVariableResolver resolver = new SystemVariableResolver();
public ClassLoader getAPIClassLoader() {
return apiCLS.getAPIClassLoader();
}
public ClassLoader getCommonClassLoader() {
return commonCLS.getCommonClassLoader();
}
public String getCommonClassPath() {
return commonCLS.getCommonClassPath();
}
public DelegatingClassLoader getConnectorClassLoader(String application) {
// For distributions where connector module (connector CL) is not available, use empty classloader with parent
if(connectorCLS != null){
return connectorCLS.getConnectorClassLoader(application);
}else{
return AccessController.doPrivileged(new PrivilegedAction<DelegatingClassLoader>() {
public DelegatingClassLoader run() {
return new DelegatingClassLoader(commonCLS.getCommonClassLoader());
}
});
}
}
public ClassLoader getAppLibClassLoader(String application, List<URI> libURIs) throws MalformedURLException {
return applibCLS.getAppLibClassLoader(application, libURIs);
}
public DelegatingClassLoader.ClassFinder getAppLibClassFinder(List<URI> libURIs) throws MalformedURLException {
return applibCLS.getAppLibClassFinder(libURIs);
}
/**
* Sets up the parent class loader for the application class loader.
* Application class loader are under the control of the ArchiveHandler since
* a special archive file format will require a specific class loader.
*
* However GlassFish needs to be able to add capabilities to the application
* like adding APIs accessibility, this is done through its parent class loader
* which we create and maintain.
*
* @param parent the parent class loader
* @param context deployment context
* @return class loader capable of loading public APIs identified by the deployers
* @throws ResolveError if one of the deployer's public API module is not found.
*/
public ClassLoader createApplicationParentCL(ClassLoader parent, DeploymentContext context)
throws ResolveError {
final ReadableArchive source = context.getSource();
List<ModuleDefinition> defs = new ArrayList<ModuleDefinition>();
// now let's see if the application is requesting any module imports
Manifest m=null;
try {
m = source.getManifest();
} catch (IOException e) {
logger.log(Level.SEVERE, "Cannot load application's manifest file :", e.getMessage());
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, e.getMessage(), e);
}
}
if (m!=null) {
String importedBundles = m.getMainAttributes().getValue(ManifestConstants.BUNDLE_IMPORT_NAME);
if (importedBundles!=null) {
for( String token : new Tokenizer(importedBundles,",")) {
Collection<HK2Module> modules = modulesRegistry.getModules(token);
if (modules.size() ==1) {
defs.add(modules.iterator().next().getModuleDefinition());
} else {
throw new ResolveError("Not able to locate a unique module by name " + token);
}
}
}
// Applications can add an additional osgi repos...
String additionalRepo = m.getMainAttributes().getValue(org.glassfish.api.ManifestConstants.GLASSFISH_REQUIRE_REPOSITORY);
if (additionalRepo != null) {
for (String token : new Tokenizer(additionalRepo, ",")) {
// Each entry should be name=path
int equals = token.indexOf('=');
if (equals == -1) {
// Missing '='...
throw new IllegalArgumentException("\""
+ org.glassfish.api.ManifestConstants.GLASSFISH_REQUIRE_REPOSITORY
+ ": " + additionalRepo + "\" is missing an '='. "
+ "It must be in the format: name=path[,name=path]...");
}
String name = token.substring(0, equals);
String path = token.substring(++equals);
addRepository(name, resolver.translate(path));
}
}
// Applications can also request to be wired to implementors of certain services.
// That means that any module implementing the requested service will be accessible
// by the parent class loader of the application.
String requestedWiring = m.getMainAttributes().getValue(org.glassfish.api.ManifestConstants.GLASSFISH_REQUIRE_SERVICES);
if (requestedWiring!=null) {
for (String token : new Tokenizer(requestedWiring, ",")) {
for (Object impl : habitat.getAllServices(BuilderHelper.createContractFilter(token))) {
HK2Module wiredBundle = modulesRegistry.find(impl.getClass());
if (wiredBundle!=null) {
defs.add(wiredBundle.getModuleDefinition());
}
}
}
}
}
if (defs.isEmpty()) {
return parent;
} else {
return modulesRegistry.getModulesClassLoader(parent, defs);
}
}
/**
* <p> This method installs the admin console OSGi bundle respository so
* our plugins can be found.</p>
*/
private void addRepository(String name, String path) {
File pathFile = new File(path);
Repository repo = new DirectoryBasedRepository(
name, pathFile);
modulesRegistry.addRepository(repo);
try {
repo.initialize();
} catch (IOException ex) {
logger.log(Level.SEVERE,
"Problem initializing additional repository!", ex);
}
}
/**
* <p> This class helps resolve ${} variables in Strings.</p>
*/
private static class SystemVariableResolver extends VariableResolver {
SystemVariableResolver() {
super();
}
protected String getVariableValue(final String varName) throws TranslationException {
String result = null;
// first look for a system property
final Object value = System.getProperty(varName);
if (value != null) {
result = "" + value;
} else {
result = "${" + varName + "}";
}
return result;
}
/**
Return true if the string is a template string of the for ${...}
*/
public static boolean needsResolving(final String value) {
return (value != null) && (value.indexOf("${") != -1);
}
/**
* Resolve the given String.
*/
public String resolve(final String value) throws TranslationException {
final String result = translate(value);
return result;
}
}
}