org.glassfish.deployment.common.DeploymentContextImpl Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation.
* Copyright (c) 2006, 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 org.glassfish.deployment.common;
import com.sun.enterprise.util.LocalStringManagerImpl;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.api.ActionReport;
import org.glassfish.api.admin.ServerEnvironment;
import org.glassfish.api.deployment.InstrumentableClassLoader;
import org.glassfish.api.deployment.OpsParams;
import org.glassfish.api.deployment.archive.ArchiveHandler;
import org.glassfish.api.deployment.archive.ReadableArchive;
import org.glassfish.deployment.versioning.VersioningUtils;
import org.glassfish.hk2.api.PreDestroy;
import org.glassfish.hk2.classmodel.reflect.Parser;
import org.glassfish.hk2.classmodel.reflect.Types;
import org.glassfish.internal.api.ClassLoaderHierarchy;
import org.glassfish.internal.deployment.Deployment;
import org.glassfish.internal.deployment.ExtendedDeploymentContext;
import org.glassfish.loader.util.ASClassLoaderUtil;
import org.glassfish.logging.annotation.LogMessagesResourceBundle;
import org.glassfish.logging.annotation.LoggerInfo;
import static com.sun.enterprise.config.serverbeans.ServerTags.IS_COMPOSITE;
import static com.sun.enterprise.util.io.FileUtils.whack;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.FINEST;
/**
* @author dochez
*/
public class DeploymentContextImpl implements ExtendedDeploymentContext, PreDestroy {
@LogMessagesResourceBundle
private static final String SHARED_LOGMESSAGE_RESOURCE = "org.glassfish.deployment.LogMessages";
// Reserve this range [NCLS-DEPLOYMENT-00001, NCLS-DEPLOYMENT-02000]
// for message ids used in this deployment common module
@LoggerInfo(subsystem = "DEPLOYMENT", description = "Deployment logger for common module", publish = true)
private static final String DEPLOYMENT_LOGGER = "jakarta.enterprise.system.tools.deployment.common";
public static final Logger deplLogger = Logger.getLogger(DEPLOYMENT_LOGGER, SHARED_LOGMESSAGE_RESOURCE);
private static final LocalStringManagerImpl localStrings = new LocalStringManagerImpl(DeploymentContextImpl.class);
private static final String INTERNAL_DIR_NAME = "__internal";
private static final String APP_TENANTS_SUBDIR_NAME = "__app-tenants";
private ReadableArchive source;
private final ReadableArchive originalSource;
private final OpsParams parameters;
private ActionReport actionReport;
private final ServerEnvironment env;
private ClassLoader cloader;
private ArchiveHandler archiveHandler;
private Properties props;
private final Map modulesMetaData = new HashMap<>();
private Phase phase = Phase.UNKNOWN;
private ClassLoader sharableTemp;
private Map modulePropsMap = new HashMap<>();
private final Map transientAppMetaData = new HashMap<>();
private final Map moduleArchiveHandlers = new HashMap<>();
private final Map moduleDeploymentContexts = new HashMap<>();
private ExtendedDeploymentContext parentContext;
private String moduleUri;
private String tenant;
private String originalAppName;
private File tenantDir;
/** Creates a new instance of DeploymentContext */
public DeploymentContextImpl(Deployment.DeploymentContextBuilder builder, ServerEnvironment env) {
this(builder.report(), builder.sourceAsArchive(), builder.params(), env);
}
public DeploymentContextImpl(ActionReport actionReport, Logger logger, ReadableArchive source, OpsParams params,
ServerEnvironment env) {
this(actionReport, source, params, env);
}
public DeploymentContextImpl(ActionReport actionReport, ReadableArchive source, OpsParams params, ServerEnvironment env) {
this.originalSource = source;
this.source = source;
this.actionReport = actionReport;
this.parameters = params;
this.env = env;
}
@Override
public Phase getPhase() {
return phase;
}
@Override
public void setPhase(Phase newPhase) {
this.phase = newPhase;
}
@Override
public ReadableArchive getSource() {
return source;
}
@Override
public void setSource(ReadableArchive source) {
this.source = source;
}
@Override
public U getCommandParameters(Class commandParametersType) {
try {
return commandParametersType.cast(parameters);
} catch (ClassCastException e) {
return null;
}
}
@Override
public Logger getLogger() {
return deplLogger;
}
@Override
public synchronized void preDestroy() {
deplLogger.log(Level.FINEST, "preDestroy()");
// FIXME: Is this really required?
try {
PreDestroy.class.cast(sharableTemp).preDestroy();
} catch (Exception e) {
// ignore, the classloader does not need to be destroyed
}
try {
PreDestroy.class.cast(cloader).preDestroy();
} catch (Exception e) {
// ignore, the classloader does not need to be destroyed
}
}
/**
* Returns the class loader associated to this deployment request. ClassLoader instances are usually obtained by the
* getClassLoader API on the associated ArchiveHandler for the archive type being deployed.
*
* This can return null and the container should allocate a ClassLoader while loading the application.
*
* @return a class loader capable of loading classes and resources from the source
* @link {org.jvnet.glassfish.apu.deployment.archive.ArchiveHandler.getClassLoader()}
*/
@Override
public ClassLoader getFinalClassLoader() {
return cloader;
}
/**
* Returns the class loader associated to this deployment request. ClassLoader instances are usually obtained by the
* getClassLoader API on the associated ArchiveHandler for the archive type being deployed.
*
* This can return null and the container should allocate a ClassLoader while loading the application.
*
* @return a class loader capable of loading classes and resources from the source
* @link {org.jvnet.glassfish.apu.deployment.archive.ArchiveHandler.getClassLoader()}
*/
@Override
public ClassLoader getClassLoader() {
/*
* TODO -- Replace this method with another that does not imply it is an accessor and conveys that the result may change
* depending on the current lifecycle. For instance contemporaryClassLoader() Problem was reported by findbug
*/
return getClassLoader(true);
}
@Override
public synchronized void setClassLoader(ClassLoader cloader) {
this.cloader = cloader;
}
// This classloader will be used for sniffer retrieval, metadata parsing
// and the prepare
@Override
public synchronized void createDeploymentClassLoader(ClassLoaderHierarchy clh, ArchiveHandler handler) throws URISyntaxException, MalformedURLException {
this.addTransientAppMetaData(IS_TEMP_CLASSLOADER, TRUE);
this.sharableTemp = createClassLoader(clh, handler, null);
}
// This classloader will used to load and start the application
@Override
public void createApplicationClassLoader(ClassLoaderHierarchy classLoaderHierarchy, ArchiveHandler handler) throws URISyntaxException, MalformedURLException {
addTransientAppMetaData(IS_TEMP_CLASSLOADER, FALSE);
if (cloader == null) {
cloader = createClassLoader(classLoaderHierarchy, handler, parameters.name());
}
}
private ClassLoader createClassLoader(ClassLoaderHierarchy classLoaderHierarchy, ArchiveHandler handler, String appName) throws URISyntaxException, MalformedURLException {
// first we create the appLib class loader, this is non shared libraries class loader
ClassLoader applibCL = classLoaderHierarchy.getAppLibClassLoader(appName, getAppLibs());
ClassLoader parentCL = classLoaderHierarchy.createApplicationParentCL(applibCL, this);
return handler.getClassLoader(parentCL, this);
}
public synchronized ClassLoader getClassLoader(boolean sharable) {
// If we are in prepare phase, we need to return our sharable temporary class loader
// otherwise, we return the final one.
if (phase == Phase.PREPARE) {
if (sharable) {
return sharableTemp;
}
return InstrumentableClassLoader.class.cast(sharableTemp).copy();
}
// we are out of the prepare phase, destroy the shareableTemp and
// return the final classloader
if (sharableTemp instanceof PreDestroy) {
try {
PreDestroy.class.cast(sharableTemp).preDestroy();
} catch (Exception e) {
getLogger().log(Level.WARNING, "ClassLoader preDestroy failed for " + sharableTemp, e);
}
}
sharableTemp = null;
return cloader;
}
/**
* Returns a scratch directory that can be used to store things in. The scratch directory will be persisted accross
* server restart but not accross redeployment of the same application
*
* @param subDirName the sub directory name of the scratch dir
* @return the scratch directory for this application based on passed in subDirName. Returns the root scratch dir if the
* passed in value is null.
*/
@Override
public File getScratchDir(String subDirName) {
File rootScratchDir = env.getApplicationStubPath();
if (tenant != null && originalAppName != null) {
// Multi-tenant case
rootScratchDir = getRootScratchTenantDirForApp(originalAppName);
rootScratchDir = new File(rootScratchDir, tenant);
if (subDirName != null) {
rootScratchDir = new File(rootScratchDir, subDirName);
}
return rootScratchDir;
}
// Regular case
if (subDirName != null) {
rootScratchDir = new File(rootScratchDir, subDirName);
}
return new File(rootScratchDir, VersioningUtils.getRepositoryName(parameters.name()));
}
/**
* {@inheritDoc}
*/
@Override
public File getSourceDir() {
return new File(getSource().getURI());
}
@Override
public void addModuleMetaData(Object metaData) {
deplLogger.log(Level.FINEST, "addModuleMetaData(metaData={0})", metaData);
if (metaData != null) {
modulesMetaData.put(metaData.getClass().getName(), metaData);
}
}
@Override
public T getModuleMetaData(Class metadataType) {
Object moduleMetaData = modulesMetaData.get(metadataType.getName());
if (moduleMetaData != null) {
return metadataType.cast(moduleMetaData);
}
for (Object metadata : modulesMetaData.values()) {
try {
return metadataType.cast(metadata);
} catch (ClassCastException e) {
}
}
return null;
}
@Override
public Collection