
org.glassfish.concurrent.runtime.ContextSetupProviderImpl Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation
* Copyright (c) 2010, 2020 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.concurrent.runtime;
import com.sun.enterprise.config.serverbeans.Application;
import com.sun.enterprise.config.serverbeans.Applications;
import com.sun.enterprise.deployment.types.ConcurrencyContextType;
import com.sun.enterprise.deployment.types.StandardContextType;
import com.sun.enterprise.security.SecurityContext;
import com.sun.enterprise.transaction.api.JavaEETransactionManager;
import com.sun.enterprise.util.Utility;
import jakarta.enterprise.concurrent.ContextService;
import jakarta.enterprise.concurrent.spi.ThreadContextSnapshot;
import jakarta.transaction.Status;
import jakarta.transaction.Transaction;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.api.invocation.ComponentInvocation;
import org.glassfish.api.invocation.InvocationManager;
import org.glassfish.concurro.spi.ContextHandle;
import org.glassfish.concurro.spi.ContextSetupProvider;
import org.glassfish.internal.data.ApplicationRegistry;
import org.glassfish.internal.deployment.Deployment;
import static com.sun.enterprise.deployment.types.StandardContextType.Classloader;
import static com.sun.enterprise.deployment.types.StandardContextType.JNDI;
import static com.sun.enterprise.deployment.types.StandardContextType.Security;
import static com.sun.enterprise.deployment.types.StandardContextType.WorkArea;
import static jakarta.enterprise.concurrent.ManagedTask.SUSPEND;
import static jakarta.enterprise.concurrent.ManagedTask.TRANSACTION;
import static jakarta.enterprise.concurrent.ManagedTask.USE_TRANSACTION_OF_EXECUTION_THREAD;
/**
* Order of calls:
*
* - {@link #saveContext(ContextService)} or {@link #saveContext(ContextService, Map)}
*
- {@link #setup}
*
- {@link #reset(ContextHandle)}
*
*/
public class ContextSetupProviderImpl implements ContextSetupProvider {
private static final long serialVersionUID = -2043616384112372091L;
private static final Logger LOG = LogFacade.getLogger();
private transient InvocationManager invocationManager;
private transient Deployment deployment;
private transient ApplicationRegistry applicationRegistry;
private transient Applications applications;
// transactionManager should be null for ContextService since it uses TransactionSetupProviderImpl
private transient JavaEETransactionManager transactionManager;
private final ContextSetup setup;
public ContextSetupProviderImpl(Set propagated, Set cleared,
Set unchanged) {
this.setup = new ContextSetup(propagated, cleared, unchanged);
ConcurrentRuntime runtime = ConcurrentRuntime.getRuntime();
this.invocationManager = runtime.getInvocationManager();
this.deployment = runtime.getDeployment();
this.applicationRegistry = runtime.getApplicationRegistry();
this.applications = runtime.getApplications();
this.transactionManager = runtime.getTransactionManager();
}
public ContextSetup getContextSetup() {
return this.setup;
}
@Override
public ContextHandle saveContext(ContextService contextService) {
return saveContext(contextService, Map.of());
}
@Override
public ContextHandle saveContext(ContextService contextService, Map executionProperties) {
LOG.log(Level.FINEST, "saveContext(contextService={0}, executionProperties={1})",
new Object[] {contextService, executionProperties});
ClassLoader classLoader = Utility.getClassLoader();
setup.reloadProviders(classLoader);
final ClassLoader contextClassloader;
if (setup.isPropagated(Classloader)) {
contextClassloader = classLoader;
} else {
contextClassloader = null;
}
final SecurityContext securityContext;
if (setup.isPropagated(Security)) {
securityContext = SecurityContext.getCurrent();
} else {
securityContext = null;
}
return createInvocationContext(executionProperties, contextClassloader, securityContext);
}
@Override
public ContextHandle setup(ContextHandle contextHandle) {
LOG.log(Level.FINEST, "setup(contextHandle={0})", contextHandle);
if (! (contextHandle instanceof InvocationContext)) {
LOG.log(Level.SEVERE, LogFacade.UNKNOWN_CONTEXT_HANDLE);
return null;
}
InvocationContext invocationCtx = (InvocationContext) contextHandle;
String appName = null;
ComponentInvocation invocation = invocationCtx.getInvocation();
if (invocation != null) {
appName = invocation.getAppName();
}
verifyApplicationEnabled(appName);
final ClassLoader resetClassLoader;
if (invocationCtx.getContextClassLoader() != null) {
resetClassLoader = Utility.setContextClassLoader(invocationCtx.getContextClassLoader());
} else {
resetClassLoader = null;
}
final SecurityContext resetSecurityContext;
if (invocationCtx.getSecurityContext() == null || setup.isUnchanged(Security)) {
resetSecurityContext = null;
} else {
resetSecurityContext = SecurityContext.getCurrent();
SecurityContext.setCurrent(invocationCtx.getSecurityContext());
}
// Each invocation needs a ResourceTableKey that returns a unique hashCode for TransactionManager
if (invocation != null) {
invocation.setResourceTableKey(new PairKey(invocation.getInstance(), Thread.currentThread()));
invocationManager.preInvoke(invocation);
}
// Ensure that there is no existing transaction in the current thread
if (transactionManager != null && setup.isClear(StandardContextType.WorkArea)) {
transactionManager.clearThreadTx();
}
ThreadMgmtData threadManagement = ThreadMgmtData.createNextGeneration(invocationCtx.getContextData());
return new InvocationContext(invocation, resetClassLoader, resetSecurityContext,
invocationCtx.isUseTransactionOfExecutionThread(), threadManagement);
}
@Override
public void reset(ContextHandle contextHandle) {
LOG.log(Level.FINEST, "reset(contextHandle={0})", contextHandle);
if (! (contextHandle instanceof InvocationContext)) {
LOG.log(Level.SEVERE, LogFacade.UNKNOWN_CONTEXT_HANDLE);
return;
}
InvocationContext invocationContext = (InvocationContext) contextHandle;
invocationContext.getContextData().endContext();
if (invocationContext.getContextClassLoader() != null) {
Utility.setContextClassLoader(invocationContext.getContextClassLoader());
}
if (invocationContext.getSecurityContext() != null) {
SecurityContext.setCurrent(invocationContext.getSecurityContext());
}
if (invocationContext.getInvocation() != null && !invocationContext.isUseTransactionOfExecutionThread()) {
invocationManager.postInvoke(invocationContext.getInvocation());
}
if (setup.isClear(WorkArea) && transactionManager != null) {
// clean up after user if a transaction is still active
// This is not required by the Concurrency spec
Transaction transaction = transactionManager.getCurrentTransaction();
if (transaction != null) {
try {
int status = transaction.getStatus();
if (status == Status.STATUS_ACTIVE) {
transactionManager.commit();
} else if (status == Status.STATUS_MARKED_ROLLBACK) {
transactionManager.rollback();
}
} catch (Exception ex) {
LOG.log(Level.SEVERE, "Cannot commit or rollback the transaction or get it's status.", ex);
}
}
transactionManager.clearThreadTx();
}
}
private ContextHandle createInvocationContext(final Map executionProperties,
final ClassLoader classloader, final SecurityContext securityCtx) {
final boolean useTxOfExecutionThread = useTransactionOfExecutionThread(executionProperties);
final List threadCtxSnapshots = setup.getThreadContextSnapshots(executionProperties);
final ComponentInvocation invocation = getSavedInvocation();
final ThreadMgmtData threadMgmtData = new ThreadMgmtData(threadCtxSnapshots);
return new InvocationContext(invocation, classloader, securityCtx, useTxOfExecutionThread, threadMgmtData);
}
private boolean useTransactionOfExecutionThread(Map executionProperties) {
return (transactionManager == null
&& USE_TRANSACTION_OF_EXECUTION_THREAD.equals(getTransactionExecutionProperty(executionProperties)))
|| setup.isUnchanged(WorkArea);
}
private String getTransactionExecutionProperty(Map executionProperties) {
if (executionProperties == null || executionProperties.isEmpty()) {
return SUSPEND;
}
return executionProperties.getOrDefault(TRANSACTION, SUSPEND);
}
private ComponentInvocation getSavedInvocation() {
ComponentInvocation currentInvocation = invocationManager.getCurrentInvocation();
if (currentInvocation != null) {
if (setup.isClear(JNDI)) {
return new ComponentInvocation();
}
if (setup.isPropagated(JNDI)) {
return cloneComponentInvocation(currentInvocation);
}
}
return null;
}
/**
* Check whether the application component submitting the task is still running.
*
* @throws IllegalStateException if application not running.
*/
private void verifyApplicationEnabled(String appId) {
if (appId != null) {
boolean enabled;
Application application = applications.getApplication(appId);
if (application != null) {
enabled = deployment.isAppEnabled(application);
} else {
// If we know the application name but don't have an application yet,
// it is starting, and thus enabled.
enabled = applicationRegistry.get(appId) != null;
}
if (!enabled) {
IllegalStateException ex = new IllegalStateException("Module " + appId + " is disabled");
LOG.log(Level.WARNING, "Context handle not in valid state. Do not run the task. " + ex.getMessage(), ex);
throw ex;
}
}
}
private ComponentInvocation cloneComponentInvocation(ComponentInvocation currentInvocation) {
ComponentInvocation clonedInvocation = currentInvocation.clone();
clonedInvocation.setResourceTableKey(null);
clonedInvocation.clearRegistry();
clonedInvocation.instance = currentInvocation.getInstance();
return clonedInvocation;
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeBoolean(transactionManager == null);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
boolean nullTransactionManager = in.readBoolean();
ConcurrentRuntime concurrentRuntime = ConcurrentRuntime.getRuntime();
// re-initialize transient fields
invocationManager = concurrentRuntime.getInvocationManager();
applicationRegistry = concurrentRuntime.getApplicationRegistry();
deployment = concurrentRuntime.getDeployment();
applications = concurrentRuntime.getApplications();
if (!nullTransactionManager) {
transactionManager = concurrentRuntime.getTransactionManager();
}
}
private static class PairKey {
private final Object instance;
private final Thread thread;
int hCode;
private PairKey(Object inst, Thread thr) {
instance = inst;
thread = thr;
if (inst != null) {
hCode = 7 * inst.hashCode();
}
if (thr != null) {
hCode += thr.hashCode();
}
}
@Override
public int hashCode() {
return hCode;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
boolean eq = false;
if (obj instanceof PairKey) {
PairKey p = (PairKey)obj;
if (instance != null) {
eq = (instance.equals(p.instance));
} else {
eq = (p.instance == null);
}
if (eq) {
if (thread != null) {
eq = (thread.equals(p.thread));
} else {
eq = (p.thread == null);
}
}
}
return eq;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy