com.sun.enterprise.v3.server.ClassLoaderHierarchyImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payara-micro Show documentation
Show all versions of payara-micro Show documentation
Micro Distribution of the Payara Project
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2007-2012 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*
*/
// Portions Copyright [2017-2021] [Payara Foundation and/or its affiliates]
package com.sun.enterprise.v3.server;
import com.sun.enterprise.loader.CurrentBeforeParentClassLoader;
import com.sun.enterprise.module.common_impl.CompositeEnumeration;
import org.glassfish.internal.api.ClassLoaderHierarchy;
import jakarta.inject.Inject;
import org.glassfish.internal.api.DelegatingClassLoader;
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.ConnectorClassLoaderService;
import org.glassfish.api.deployment.archive.ReadableArchive;
import org.glassfish.api.deployment.DeploymentContext;
import java.net.URI;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.Enumeration;
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.HK2Module;
import com.sun.enterprise.module.common_impl.DirectoryBasedRepository;
import com.sun.enterprise.module.common_impl.Tokenizer;
/**
* @author [email protected]
*/
@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();
@Override
public ClassLoader getAPIClassLoader() {
return apiCLS.getAPIClassLoader();
}
@Override
public CurrentBeforeParentClassLoader getCommonClassLoader() {
return commonCLS.getCommonClassLoader();
}
@Override
public String getCommonClassPath() {
return commonCLS.getCommonClassPath();
}
@Override
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() {
public DelegatingClassLoader run() {
return new DelegatingClassLoader(commonCLS.getCommonClassLoader());
}
});
}
}
@Override
public ClassLoader getAppLibClassLoader(String application, List libURIs) throws MalformedURLException {
return applibCLS.getAppLibClassLoader(application, libURIs);
}
@Override
public DelegatingClassLoader.ClassFinder getAppLibClassFinder(List 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.
*/
@Override
public ClassLoader createApplicationParentCL(ClassLoader parent, DeploymentContext context)
throws ResolveError {
final ReadableArchive source = context.getSource();
List defs = new ArrayList();
// 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 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 {
// modules formed from requirements need to have higher priority than application classloader, so that
// admin gui can override faces implementation that resides in API ClassLoader
ClassLoader preferredClassLoader = modulesRegistry.getModulesClassLoader(modulesRegistry.getParentClassLoader(), defs);
// DelegatingClassLoader does exist in the codebase, but it has requirement that all delegates have same parent
// which is exactly what we do not want to have here as that parent would be API ClassLoader.
return new SimpleDelegatingClassLoader(modulesRegistry.getParentClassLoader(), preferredClassLoader, parent);
}
}
/**
* This method installs the admin console OSGi bundle respository so
* our plugins can be found.
*/
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);
}
}
/**
* This class helps resolve ${} variables in Strings.
*/
private static class SystemVariableResolver extends VariableResolver {
SystemVariableResolver() {
super();
}
@Override
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.contains("${"));
}
/**
* Resolve the given String.
*/
public String resolve(final String value) throws TranslationException {
final String result = translate(value);
return result;
}
}
private static class SimpleDelegatingClassLoader extends ClassLoader {
private final ClassLoader first;
private final ClassLoader second;
public SimpleDelegatingClassLoader(ClassLoader parent, ClassLoader first, ClassLoader second) {
super(parent);
this.first = first;
this.second = second;
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
try {
return first.loadClass(name);
} catch (ClassNotFoundException cne) {
return second.loadClass(name);
}
}
@Override
protected URL findResource(String name) {
URL result = first.getResource(name);
if (result == null) {
return second.getResource(name);
}
return result;
}
@Override
protected Enumeration findResources(String name) throws IOException {
return new CompositeEnumeration(Arrays.asList(first.getResources(name), second.getResources(name)));
}
}
}