diff --git a/container/openejb-core/pom.xml b/container/openejb-core/pom.xml index 734b6ffeded..a07a3d679d5 100644 --- a/container/openejb-core/pom.xml +++ b/container/openejb-core/pom.xml @@ -777,4 +777,3 @@ - diff --git a/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java b/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java index 1ae5963e638..40644a9e834 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java @@ -124,6 +124,7 @@ import org.apache.openejb.spi.ApplicationServer; import org.apache.openejb.spi.ContainerSystem; import org.apache.openejb.spi.SecurityService; +import org.apache.openejb.threads.impl.ContextServiceImplFactory; import org.apache.openejb.threads.impl.ManagedExecutorServiceImpl; import org.apache.openejb.util.Contexts; import org.apache.openejb.util.DaemonThreadFactory; @@ -1783,7 +1784,7 @@ private void ensureWebBeansContext(final AppContext appContext) { final Map, Object> services = new HashMap<>(); - services.put(Executor.class, new ManagedExecutorServiceImpl(ForkJoinPool.commonPool())); + services.put(Executor.class, new ManagedExecutorServiceImpl(ForkJoinPool.commonPool(), ContextServiceImplFactory.newPropagateEverythingContextService())); services.put(JNDIService.class, new OpenEJBJndiService()); services.put(AppContext.class, appContext); services.put(ScannerService.class, new CdiScanner()); diff --git a/container/openejb-core/src/main/java/org/apache/openejb/cdi/CdiBeanInfo.java b/container/openejb-core/src/main/java/org/apache/openejb/cdi/CdiBeanInfo.java index d641b4d48d8..d16f8f4b4b8 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/cdi/CdiBeanInfo.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/cdi/CdiBeanInfo.java @@ -18,6 +18,7 @@ package org.apache.openejb.cdi; import org.apache.openejb.Injection; +import org.apache.openejb.jee.ContextService; import org.apache.openejb.jee.DataSource; import org.apache.openejb.jee.EjbLocalRef; import org.apache.openejb.jee.EjbRef; @@ -65,6 +66,7 @@ public class CdiBeanInfo implements JndiConsumer { private String beanName; private ClassLoader classLoader; private List injections; + private KeyedCollection contextService; public String getBeanName() { return beanName; @@ -321,4 +323,11 @@ public String getJndiConsumerName() { public Class getBeanClass() { return this.beanClass; } + @Override + public Map getContextServiceMap() { + if (contextService == null) { + contextService = new KeyedCollection(); + } + return this.contextService.toMap(); + } } diff --git a/container/openejb-core/src/main/java/org/apache/openejb/cdi/ThreadSingletonServiceImpl.java b/container/openejb-core/src/main/java/org/apache/openejb/cdi/ThreadSingletonServiceImpl.java index 45662bf4d4d..e0906d90518 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/cdi/ThreadSingletonServiceImpl.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/cdi/ThreadSingletonServiceImpl.java @@ -26,6 +26,7 @@ import org.apache.openejb.loader.event.ComponentAdded; import org.apache.openejb.loader.event.ComponentRemoved; import org.apache.openejb.observer.Observes; +import org.apache.openejb.threads.impl.ContextServiceImplFactory; import org.apache.openejb.threads.impl.ManagedExecutorServiceImpl; import org.apache.openejb.threads.impl.ManagedThreadFactoryImpl; import org.apache.openejb.util.AppFinder; @@ -178,7 +179,7 @@ public void execute(final Runnable command) { .size(3) .threadFactory(new ManagedThreadFactoryImpl(appContext.getId() + "-cdi-fireasync-")) .prefix("CDIAsyncPool") - .build(appContext.getOptions())); + .build(appContext.getOptions()), ContextServiceImplFactory.newPropagateEverythingContextService()); delegate.compareAndSet(null, executor); } else { executor = alreadyUpdated; diff --git a/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java b/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java index 750890f8a2e..9865432f5fe 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java @@ -17,6 +17,7 @@ package org.apache.openejb.config; +import jakarta.enterprise.concurrent.ContextServiceDefinition; import jakarta.interceptor.AroundConstruct; import org.apache.openejb.BeanContext; import org.apache.openejb.OpenEJBException; @@ -47,6 +48,7 @@ import org.apache.openejb.jee.ConfigProperty; import org.apache.openejb.jee.ContainerConcurrency; import org.apache.openejb.jee.ContainerTransaction; +import org.apache.openejb.jee.ContextService; import org.apache.openejb.jee.DataSource; import org.apache.openejb.jee.EjbJar; import org.apache.openejb.jee.EjbLocalRef; @@ -121,6 +123,7 @@ import org.apache.openejb.jee.TransactionType; import org.apache.openejb.jee.WebApp; import org.apache.openejb.jee.WebserviceDescription; +import org.apache.openejb.jee.jba.JndiName; import org.apache.openejb.jee.oejb3.OpenejbJar; import org.apache.openejb.loader.JarLocation; import org.apache.openejb.loader.SystemInstance; @@ -4062,6 +4065,22 @@ public void buildAnnotatedRefs(final JndiConsumer consumer, final IAnnotationFin buildDataSourceDefinition(consumer, definition); } + // + // @ContextServiceDefinition + // + + for (final Annotated> annotated : annotationFinder.findMetaAnnotatedClasses(ContextServiceDefinition.List.class)) { + final ContextServiceDefinition.List defs = annotated.getAnnotation(ContextServiceDefinition.List.class); + for (final ContextServiceDefinition definition : defs.value()) { + buildContextServiceDefinition(consumer, definition); + } + } + + for (final Annotated> annotated : annotationFinder.findMetaAnnotatedClasses(ContextServiceDefinition.class)) { + final ContextServiceDefinition definition = annotated.getAnnotation(ContextServiceDefinition.class); + buildContextServiceDefinition(consumer, definition); + } + // // @JMSConnectionFactoryDefinition // @@ -4093,6 +4112,31 @@ public void buildAnnotatedRefs(final JndiConsumer consumer, final IAnnotationFin } } + private void buildContextServiceDefinition(final JndiConsumer consumer, final ContextServiceDefinition definition) { + final ContextService existing = consumer.getContextServiceMap().get(definition.name()); + final ContextService contextService = (existing != null) ? existing : new ContextService(); + + if (contextService.getName() == null) { + final JndiName jndiName = new JndiName(); + jndiName.setvalue(definition.name()); + contextService.setName(jndiName); + } + + if (contextService.getCleared().isEmpty()) { + contextService.getCleared().addAll(Arrays.asList(definition.cleared())); + } + + if (contextService.getPropagated().isEmpty()) { + contextService.getPropagated().addAll(Arrays.asList(definition.propagated())); + } + + if (contextService.getUnchanged().isEmpty()) { + contextService.getUnchanged().addAll(Arrays.asList(definition.unchanged())); + } + + consumer.getContextServiceMap().put(definition.name(), contextService); + } + private void buildContext(final JndiConsumer consumer, final Member member) { final ContextRef ref = new ContextRef(); ref.setName(member.getDeclaringClass().getName() + "/" + member.getName()); diff --git a/container/openejb-core/src/main/java/org/apache/openejb/config/AutoConfig.java b/container/openejb-core/src/main/java/org/apache/openejb/config/AutoConfig.java index 90727d62785..3e7c2a7e151 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/config/AutoConfig.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/config/AutoConfig.java @@ -2209,6 +2209,14 @@ private String installResource(final String beanName, final ResourceInfo resourc } } + final String contextId = resourceInfo.properties.getProperty("Context"); + if (contextId != null && contextId.length() > 0) { + final String newResourceId = getResourceId(beanName, contextId, null, null); + if (!contextId.equals(newResourceId)) { + resourceInfo.properties.setProperty("Context", newResourceId); + } + } + configFactory.install(resourceInfo); return resourceInfo.id; } diff --git a/container/openejb-core/src/main/java/org/apache/openejb/config/BuiltInEnvironmentEntries.java b/container/openejb-core/src/main/java/org/apache/openejb/config/BuiltInEnvironmentEntries.java index a540ada9083..9e926a73af1 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/config/BuiltInEnvironmentEntries.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/config/BuiltInEnvironmentEntries.java @@ -92,25 +92,33 @@ private void add(final JndiConsumer jndi, final DeploymentModule module, final D add(jndi.getResourceEnvRefMap(), new ResourceEnvRef().name("java:comp/TransactionSynchronizationRegistry").type(TransactionSynchronizationRegistry.class)); if (defaults) { + // From: https://jakarta.ee/specifications/concurrency/3.0/jakarta-concurrency-spec-3.0.pdf + // Jakarta Concurrency §3.1.4.3 add(jndi.getResourceEnvRefMap(), new ResourceEnvRef().name("java:comp/DefaultManagedExecutorService").type(ManagedExecutorService.class)); + + // Jakarta Concurrency §3.2.4.3 add(jndi.getResourceEnvRefMap(), new ResourceEnvRef().name("java:comp/DefaultManagedScheduledExecutorService").type(ManagedScheduledExecutorService.class)); + + // Jakarta Concurrency §3.4.4.3 add(jndi.getResourceEnvRefMap(), new ResourceEnvRef().name("java:comp/DefaultManagedThreadFactory").type(ManagedThreadFactory.class)); + + // Jakarta Concurrency §3.3.4.3 add(jndi.getResourceEnvRefMap(), new ResourceEnvRef().name("java:comp/DefaultContextService").type(ContextService.class)); + + try { final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); contextClassLoader.loadClass("org.apache.activemq.ActiveMQSslConnectionFactory"); final ResourceEnvRef ref = new ResourceEnvRef().name("java:comp/DefaultJMSConnectionFactory") - .type(contextClassLoader.loadClass("jakarta.jms.ConnectionFactory")); + .type(contextClassLoader.loadClass("jakarta.jms.ConnectionFactory")); add(jndi.getResourceEnvRefMap(), ref); } catch (final ClassNotFoundException | NoClassDefFoundError notThere) { // no-op } } - // OpenEJB specific feature add(jndi.getEnvEntryMap(), new EnvEntry().name("java:comp/ComponentName").value(jndi.getJndiConsumerName()).type(String.class)); - } private void add(final Map map, final E entry) { diff --git a/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java b/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java index b8b5e9eb742..f94d6b4344a 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java @@ -287,6 +287,7 @@ public ConfigurationFactory(final boolean offline, final DynamicDeployer preAuto } chain.add(new ConvertDataSourceDefinitions()); + chain.add(new ConvertContextServiceDefinitions()); chain.add(new ConvertJMSConnectionFactoryDefinitions()); chain.add(new ConvertJMSDestinationDefinitions()); chain.add(new CleanEnvEntries()); diff --git a/container/openejb-core/src/main/java/org/apache/openejb/config/ConvertContextServiceDefinitions.java b/container/openejb-core/src/main/java/org/apache/openejb/config/ConvertContextServiceDefinitions.java new file mode 100755 index 00000000000..edf22723236 --- /dev/null +++ b/container/openejb-core/src/main/java/org/apache/openejb/config/ConvertContextServiceDefinitions.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.openejb.config; + +import org.apache.openejb.OpenEJBException; +import org.apache.openejb.config.sys.Resource; +import org.apache.openejb.jee.ContextService; +import org.apache.openejb.jee.JndiConsumer; +import org.apache.openejb.jee.KeyedCollection; +import org.apache.openejb.util.Join; +import org.apache.openejb.util.PropertyPlaceHolderHelper; + +import java.util.List; +import java.util.Map; +import java.util.Properties; + +/** + * @version $Rev$ $Date$ + */ +public class ConvertContextServiceDefinitions extends BaseConvertDefinitions { + + @Override + public AppModule deploy(final AppModule appModule) throws OpenEJBException { + + final List jndiConsumers = collectConsumers(appModule); + + final KeyedCollection contextServices = new KeyedCollection<>(); + final KeyedCollection contextServicesFromCompManagedBeans = new KeyedCollection<>(); + + for (final JndiConsumer consumer : jndiConsumers) { + if (consumer == null) { + continue; + } + + if (consumer instanceof CompManagedBean) { + /* + * TOMEE-2053: It may contain invalid context service definitions + * because it is never updated with content from the ejb-jar.xml + * Wait until all other consumers have been processed, to safely + * decide which context services to transfer; + */ + + contextServicesFromCompManagedBeans.addAll(consumer.getContextServiceMap().values()); + continue; + } + contextServices.addAll(consumer.getContextServiceMap().values()); + } + + final Map dataSourcesMap = contextServices.toMap(); + for(ContextService contextService : contextServicesFromCompManagedBeans){ + //Interested only in ContextServices that come from non-JndiConsumers + if(!dataSourcesMap.containsKey(contextService.getName().getvalue())){ + contextServices.add(contextService); + } + } + + for (final ContextService dataSource : contextServices) { + appModule.getResources().add(toResource(dataSource)); + } + return appModule; + } + + + private Resource toResource(final ContextService contextService) { + final String name = cleanUpName(contextService.getName().getvalue()); + + final Resource def = new Resource(name, jakarta.enterprise.concurrent.ContextService.class.getName()); + + def.setJndi(contextService.getName().getvalue().replaceFirst("java:", "")); + def.setType(jakarta.enterprise.concurrent.ContextService.class.getName()); + + final Properties p = def.getProperties(); + put(p, "propagated", Join.join(",", contextService.getPropagated())); + put(p, "cleared", Join.join(",", contextService.getCleared())); + put(p, "unchanged", Join.join(",", contextService.getUnchanged())); + + // to force it to be bound in JndiEncBuilder + put(p, "JndiName", def.getJndi()); + + return def; + } + + private static void put(final Properties properties, final String key, final Object value) { + if (key == null) { + return; + } + if (value == null) { + return; + } + + properties.put(key, PropertyPlaceHolderHelper.value(String.valueOf(value))); + } +} diff --git a/container/openejb-core/src/main/java/org/apache/openejb/core/BaseContext.java b/container/openejb-core/src/main/java/org/apache/openejb/core/BaseContext.java index b91eca9ca38..f8a2a2c7cf3 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/core/BaseContext.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/core/BaseContext.java @@ -74,8 +74,7 @@ protected BaseContext(final SecurityService securityService, final UserTransacti } private boolean isAsyncOperation(final ThreadContext threadContext) { - if (threadContext.getCurrentOperation() == null - && threadContext.get(CUTask.Context.class) != null) { + if (threadContext.getCurrentOperation() == null) { return true; } return false; diff --git a/container/openejb-core/src/main/java/org/apache/openejb/core/ThreadContext.java b/container/openejb-core/src/main/java/org/apache/openejb/core/ThreadContext.java index 2a5d57f0ea8..ec04b96d5bb 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/core/ThreadContext.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/core/ThreadContext.java @@ -39,6 +39,13 @@ public static ThreadContext getThreadContext() { return threadStorage.get(); } + public static ThreadContext clear() { + final ThreadContext oldContext = threadStorage.get(); + threadStorage.set(null); + return oldContext; + } + + public static ThreadContext enter(final ThreadContext newContext) { if (newContext == null) { throw new NullPointerException("newContext is null"); diff --git a/container/openejb-core/src/main/java/org/apache/openejb/persistence/PersistenceUnitInfoImpl.java b/container/openejb-core/src/main/java/org/apache/openejb/persistence/PersistenceUnitInfoImpl.java index cd577e36108..fd6188f9d29 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/persistence/PersistenceUnitInfoImpl.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/persistence/PersistenceUnitInfoImpl.java @@ -363,7 +363,8 @@ public PersistenceClassFileTransformer(final ClassTransformer classTransformer) this.classTransformer = classTransformer; } - public byte[] transform(final ClassLoader classLoader, final String className, final Class classBeingRedefined, final ProtectionDomain protectionDomain, final byte[] classfileBuffer) throws IllegalClassFormatException { + public byte[] transform(final ClassLoader classLoader, final String className, final Class classBeingRedefined, + final ProtectionDomain protectionDomain, final byte[] classfileBuffer) throws IllegalClassFormatException { // Example code to easily debug transformation of a specific class // if ("org/apache/openejb/test/entity/cmp/BasicCmpBean".equals(className) || // "org/apache/openejb/test/entity/cmp/BasicCmp2Bean_BasicCmp2Bean".equals(className)) { diff --git a/container/openejb-core/src/main/java/org/apache/openejb/resource/thread/ManagedExecutorServiceImplFactory.java b/container/openejb-core/src/main/java/org/apache/openejb/resource/thread/ManagedExecutorServiceImplFactory.java index 499e7f04f4f..fa2981e995b 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/resource/thread/ManagedExecutorServiceImplFactory.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/resource/thread/ManagedExecutorServiceImplFactory.java @@ -16,6 +16,9 @@ */ package org.apache.openejb.resource.thread; +import org.apache.openejb.loader.SystemInstance; +import org.apache.openejb.spi.ContainerSystem; +import org.apache.openejb.threads.impl.ContextServiceImpl; import org.apache.openejb.threads.impl.ManagedExecutorServiceImpl; import org.apache.openejb.threads.impl.ManagedThreadFactoryImpl; import org.apache.openejb.threads.reject.CURejectHandler; @@ -24,6 +27,10 @@ import org.apache.openejb.util.Logger; import jakarta.enterprise.concurrent.ManagedThreadFactory; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.sql.DataSource; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; @@ -37,8 +44,10 @@ public class ManagedExecutorServiceImplFactory { private int queue = 15; private String threadFactory; + private String context; + public ManagedExecutorServiceImpl create() { - return new ManagedExecutorServiceImpl(createExecutorService()); + return new ManagedExecutorServiceImpl(createExecutorService(), findContextService()); } private ExecutorService createExecutorService() { @@ -62,6 +71,25 @@ private ExecutorService createExecutorService() { return new ThreadPoolExecutor(core, max, keepAlive.getTime(), keepAlive.getUnit(), blockingQueue, managedThreadFactory, CURejectHandler.INSTANCE); } + private ContextServiceImpl findContextService() { + if (context == null || context.trim().isEmpty()) { + throw new IllegalArgumentException("Please specify a context service to be used with the managed executor"); + } + + try { + final ContainerSystem containerSystem = SystemInstance.get().getComponent(ContainerSystem.class); + final Context context = containerSystem.getJNDIContext(); + final Object obj = context.lookup("openejb/Resource/" + this.context); + if (!(obj instanceof ContextServiceImpl)) { + throw new IllegalArgumentException("Resource with id " + context + + " is not a ContextService, but is " + obj.getClass().getName()); + } + return (ContextServiceImpl) obj; + } catch (final NamingException e) { + throw new IllegalArgumentException("Unknown context service " + context); + } + } + public void setCore(final int core) { this.core = core; } @@ -81,4 +109,12 @@ public void setQueue(final int queue) { public void setThreadFactory(final String threadFactory) { this.threadFactory = threadFactory; } + + public String getContext() { + return context; + } + + public void setContext(final String context) { + this.context = context; + } } diff --git a/container/openejb-core/src/main/java/org/apache/openejb/resource/thread/ManagedScheduledExecutorServiceImplFactory.java b/container/openejb-core/src/main/java/org/apache/openejb/resource/thread/ManagedScheduledExecutorServiceImplFactory.java index 8f5186d9eb7..d6f83304cc2 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/resource/thread/ManagedScheduledExecutorServiceImplFactory.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/resource/thread/ManagedScheduledExecutorServiceImplFactory.java @@ -16,6 +16,10 @@ */ package org.apache.openejb.resource.thread; +import org.apache.openejb.loader.SystemInstance; +import org.apache.openejb.spi.ContainerSystem; +import org.apache.openejb.threads.impl.ContextServiceImpl; +import org.apache.openejb.threads.impl.ContextServiceImplFactory; import org.apache.openejb.threads.impl.ManagedScheduledExecutorServiceImpl; import org.apache.openejb.threads.impl.ManagedThreadFactoryImpl; import org.apache.openejb.threads.reject.CURejectHandler; @@ -23,6 +27,9 @@ import org.apache.openejb.util.Logger; import jakarta.enterprise.concurrent.ManagedThreadFactory; + +import javax.naming.Context; +import javax.naming.NamingException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -30,8 +37,32 @@ public class ManagedScheduledExecutorServiceImplFactory { private int core = 5; private String threadFactory = ManagedThreadFactoryImpl.class.getName(); + private String context; + + public ManagedScheduledExecutorServiceImpl create(final ContextServiceImpl contextService) { + return new ManagedScheduledExecutorServiceImpl(createScheduledExecutorService(), contextService); + } public ManagedScheduledExecutorServiceImpl create() { - return new ManagedScheduledExecutorServiceImpl(createScheduledExecutorService()); + return new ManagedScheduledExecutorServiceImpl(createScheduledExecutorService(), findContextService()); + } + + private ContextServiceImpl findContextService() { + if (context == null || context.trim().isEmpty()) { + throw new IllegalArgumentException("Please specify a context service to be used with the managed executor"); + } + + try { + final ContainerSystem containerSystem = SystemInstance.get().getComponent(ContainerSystem.class); + final Context context = containerSystem.getJNDIContext(); + final Object obj = context.lookup("openejb/Resource/" + this.context); + if (!(obj instanceof ContextServiceImpl)) { + throw new IllegalArgumentException("Resource with id " + context + + " is not a ContextService, but is " + obj.getClass().getName()); + } + return (ContextServiceImpl) obj; + } catch (final NamingException e) { + throw new IllegalArgumentException("Unknown context service " + context); + } } private ScheduledExecutorService createScheduledExecutorService() { @@ -53,4 +84,12 @@ public void setCore(final int core) { public void setThreadFactory(final String threadFactory) { this.threadFactory = threadFactory; } + + public String getContext() { + return context; + } + + public void setContext(String context) { + this.context = context; + } } diff --git a/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ApplicationThreadContextProvider.java b/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ApplicationThreadContextProvider.java new file mode 100755 index 00000000000..85e4bb1eddf --- /dev/null +++ b/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ApplicationThreadContextProvider.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openejb.threads.impl; + +import jakarta.enterprise.concurrent.ContextServiceDefinition; +import jakarta.enterprise.concurrent.spi.ThreadContextProvider; +import jakarta.enterprise.concurrent.spi.ThreadContextRestorer; +import jakarta.enterprise.concurrent.spi.ThreadContextSnapshot; +import org.apache.openejb.core.ThreadContext; + +import java.util.Map; + +public class ApplicationThreadContextProvider implements ThreadContextProvider { + @Override + public ThreadContextSnapshot currentContext(final Map props) { + // TODO: is there anything we need to mess around with here? ClassLoader? + return new ApplicationThreadContextSnapshot(ThreadContext.getThreadContext()); + } + + @Override + public ThreadContextSnapshot clearedContext(final Map props) { + return new ApplicationThreadContextSnapshot(null); + } + + @Override + public String getThreadContextType() { + return ContextServiceDefinition.APPLICATION; + } + + public class ApplicationThreadContextSnapshot implements ThreadContextSnapshot { + + private final ThreadContext threadContext; + + public ApplicationThreadContextSnapshot(final ThreadContext threadContext) { + this.threadContext = threadContext; + } + + @Override + public ThreadContextRestorer begin() { + final ThreadContext restoreContext = (threadContext == null) ? + ThreadContext.clear() : + ThreadContext.enter(threadContext); + + return new ApplicationThreadContextRestorer(restoreContext); + } + } + + public class ApplicationThreadContextRestorer implements ThreadContextRestorer { + + private final ThreadContext restoreContext; + + public ApplicationThreadContextRestorer(final ThreadContext restoreContext) { + this.restoreContext = restoreContext; + } + + @Override + public void endContext() throws IllegalStateException { + if (restoreContext != null) { + ThreadContext.exit(restoreContext); + } + } + } +} diff --git a/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ContextServiceImpl.java b/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ContextServiceImpl.java index a1a06a1e35c..374eeea10a6 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ContextServiceImpl.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ContextServiceImpl.java @@ -16,17 +16,24 @@ */ package org.apache.openejb.threads.impl; +import jakarta.enterprise.concurrent.ContextServiceDefinition; +import jakarta.enterprise.concurrent.spi.ThreadContextProvider; +import jakarta.enterprise.concurrent.spi.ThreadContextRestorer; +import jakarta.enterprise.concurrent.spi.ThreadContextSnapshot; import org.apache.openejb.OpenEJB; import org.apache.openejb.threads.task.CUTask; import jakarta.enterprise.concurrent.ContextService; import jakarta.enterprise.concurrent.ManagedTask; import jakarta.transaction.Transaction; + import java.io.Serializable; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; @@ -39,8 +46,25 @@ import java.util.function.Supplier; public class ContextServiceImpl implements ContextService { + private static final HashMap EMPTY_PROPS = new HashMap(); + private final List propagated = new ArrayList<>(); + private final List cleared = new ArrayList<>(); + private final List unchanged = new ArrayList<>(); + + public List getPropagated() { + return propagated; + } + + public List getCleared() { + return cleared; + } + + public List getUnchanged() { + return unchanged; + } + @Override public Callable contextualCallable(final Callable callable) { return createContextualProxy(callable, Callable.class); @@ -116,13 +140,88 @@ public CompletionStage withContextCapture(final CompletionStage comple return createContextualProxy(completionStage, CompletionStage.class); } - private static final class CUHandler extends CUTask implements InvocationHandler, Serializable { + public Snapshot snapshot(final Map props) { + final List snapshots = new ArrayList<>(); + + // application context needs to be applied first + + boolean appContextPropagated; + ThreadContextProvider appContext = find(ContextServiceDefinition.APPLICATION, propagated); + if (appContext != null) { + appContextPropagated = true; + } else { + appContext = find(ContextServiceDefinition.APPLICATION, cleared); + appContextPropagated = false; + } + + if (appContext != null) { + if (appContextPropagated) { + snapshots.add(appContext.currentContext(props)); + } else { + snapshots.add(appContext.clearedContext(props)); + } + } + + for (ThreadContextProvider threadContextProvider : propagated) { + if (ContextServiceDefinition.APPLICATION.equals(threadContextProvider.getThreadContextType())) + continue; + + final ThreadContextSnapshot snapshot = threadContextProvider.currentContext(props); + snapshots.add(snapshot); + } + + for (ThreadContextProvider threadContextProvider : cleared) { + if (ContextServiceDefinition.APPLICATION.equals(threadContextProvider.getThreadContextType())) + continue; + + final ThreadContextSnapshot snapshot = threadContextProvider.clearedContext(props); + snapshots.add(snapshot); + } + + return new Snapshot(snapshots); + } + + private ThreadContextProvider find(final String name, final List threadContextProviders) { + for (final ThreadContextProvider threadContextProvider : threadContextProviders) { + if (name.equals(threadContextProvider.getThreadContextType())) { + return threadContextProvider; + } + } + + return null; + } + + public State enter(final Snapshot snapshot) { + + final List restorers = new ArrayList<>(); + + for (ThreadContextSnapshot tcs : snapshot.getSnapshots()) { + try { + restorers.add(0, tcs.begin()); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + return new State(restorers); + } + + public void exit(final State state) { + if (state != null) { + final List restorers = state.getRestorers(); + for (ThreadContextRestorer restorer : restorers) { + restorer.endContext(); + } + } + } + + private final class CUHandler extends CUTask implements InvocationHandler, Serializable { private final Object instance; private final Map properties; private final boolean suspendTx; private CUHandler(final Object instance, final Map props) { - super(instance); + super(instance, ContextServiceImpl.this); this.instance = instance; this.properties = props; this.suspendTx = ManagedTask.SUSPEND.equals(props.get(ManagedTask.TRANSACTION)); @@ -155,4 +254,27 @@ public Object call() throws Exception { } } } + + public class State { + private final List restorers; + + public State(final List restorers) { + this.restorers = restorers; + } + + public List getRestorers() { + return restorers; + } + } + public class Snapshot { + private final List snapshots; + + public Snapshot(final List snapshots) { + this.snapshots = snapshots; + } + + public List getSnapshots() { + return snapshots; + } + } } diff --git a/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ContextServiceImplFactory.java b/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ContextServiceImplFactory.java new file mode 100755 index 00000000000..2b5fcecd7b1 --- /dev/null +++ b/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ContextServiceImplFactory.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openejb.threads.impl; + +import jakarta.enterprise.concurrent.ContextServiceDefinition; +import jakarta.enterprise.concurrent.spi.ThreadContextProvider; +import org.apache.openejb.util.Join; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; + +public class ContextServiceImplFactory { + + private final List propagated = new ArrayList<>(); + private final List cleared = new ArrayList<>(); + private final List unchanged = new ArrayList<>(); + + public String getPropagated() { + return Join.join(",", propagated); + } + + public void setPropagated(final String propagated) { + this.propagated.clear(); + if (propagated != null && propagated.length() > 0) { + this.propagated.addAll(Arrays.asList(propagated.split(" *, *"))); + } + } + + public String getCleared() { + return Join.join(",", cleared); + } + + public void setCleared(final String cleared) { + this.cleared.clear(); + if (cleared != null && cleared.length() > 0) { + this.cleared.addAll(Arrays.asList(cleared.split(" *, *"))); + } + } + + public String getUnchanged() { + return Join.join(",", unchanged); + } + + public void setUnchanged(final String unchanged) { + this.unchanged.clear(); + if (unchanged != null && unchanged.length() > 0) { + this.unchanged.addAll(Arrays.asList(unchanged.split(" *, *"))); + } + } + + public static ContextServiceImpl newDefaultContextService() { + return new ContextServiceImplFactory().create(); + } + + public static ContextServiceImpl newPropagateEverythingContextService() { + final ContextServiceImplFactory factory = new ContextServiceImplFactory(); + factory.setPropagated(ContextServiceDefinition.ALL_REMAINING); + return factory.create(); + } + + + public ContextServiceImpl create() { + // some complication around this is ContextServiceDefinition.ALL_REMAINING, which looks like it + // should reference everything that isn't explicitly mentioned in this.propagated, this.cleared and this.unchanged. + + // These are the defaults: + + // String[] propagated() default { ALL_REMAINING }; + // String[] cleared() default { TRANSACTION }; + // String[] unchanged() default {}; + + // These are specified on the annotation, but we could also apply TRANSACTION to cleared, and ALL_REMAINING to propagated + // if they are not explicitly set anywhere + + // not sure if there is some sort of priority if a context is listed in more than one bucket..., + // let's assume propagated, cleared, unchanged is the order + + // Let's build a list of stuff + + + final Map threadContextProviders = new HashMap<>(); + + // add the in-build ThreadContextProviders + threadContextProviders.put(ContextServiceDefinition.APPLICATION, new ApplicationThreadContextProvider()); + threadContextProviders.put(ContextServiceDefinition.SECURITY, new SecurityThreadContextProvider()); + threadContextProviders.put(ContextServiceDefinition.TRANSACTION, new TxThreadContextProvider()); + getThreadContextProviders().forEach(t -> threadContextProviders.putIfAbsent(t.getThreadContextType(), t)); + + // Let's resolve what should actually be in each bucket: + // * specified contexts that don't actually exist should be ignored + // * ALL_REMAINING should be changed into a list of contexts that actually are remaining + + final List resolvedPropagated = new ArrayList<>(); + final List resolvedCleared = new ArrayList<>(); + final List resolvedUnchanged = new ArrayList<>(); + + resolve(resolvedPropagated, propagated, threadContextProviders); + resolve(resolvedCleared, cleared, threadContextProviders); + resolve(resolvedUnchanged, unchanged, threadContextProviders); + + if (propagated.contains(ContextServiceDefinition.ALL_REMAINING)) { + resolvedPropagated.addAll(threadContextProviders.values()); + threadContextProviders.clear(); + } + + if (cleared.contains(ContextServiceDefinition.ALL_REMAINING)) { + resolvedCleared.addAll(threadContextProviders.values()); + threadContextProviders.clear(); + } + + if (unchanged.contains(ContextServiceDefinition.ALL_REMAINING)) { + resolvedUnchanged.addAll(threadContextProviders.values()); + threadContextProviders.clear(); + } + + // check if anything is left: TRANSACTION should go on cleared, everything else + // should go on propagated + + if (threadContextProviders.containsKey(ContextServiceDefinition.TRANSACTION)) { + resolvedCleared.add(threadContextProviders.remove(ContextServiceDefinition.TRANSACTION)); + } + + resolvedPropagated.addAll(threadContextProviders.values()); + threadContextProviders.clear(); + + // TODO: we could log the awesome work we have done to figure all this out + // TODO: additionally, this should all be incredibly easy to unit test + +// boolean suspendTx = resolvedCleared.contains(ContextServiceDefinition.TRANSACTION); +// +// // this takes precedence over where ContextServiceDefinition.TRANSACTION is set +// if (ManagedTask.SUSPEND.equals(properties.get(ManagedTask.TRANSACTION))) { +// suspendTx = true; +// } + + final ContextServiceImpl contextService = new ContextServiceImpl(); + contextService.getPropagated().addAll(resolvedPropagated); + contextService.getCleared().addAll(resolvedCleared); + contextService.getUnchanged().addAll(resolvedUnchanged); + + return contextService; + } + + protected List getThreadContextProviders() { + final List result = new ArrayList<>(); + for (ThreadContextProvider tcp : ServiceLoader.load(ThreadContextProvider.class)) { + result.add(tcp); + } + + return result; + } + + private void resolve(final List providers, final List specified, final Map availableProviders) { + for (String specifiedProviderName : specified) { + if (availableProviders.containsKey(specifiedProviderName)) { + providers.add(availableProviders.remove(specifiedProviderName)); + } + } + } +} diff --git a/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ManagedExecutorServiceImpl.java b/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ManagedExecutorServiceImpl.java index 1bfae1aeb2f..7479d06c3f6 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ManagedExecutorServiceImpl.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ManagedExecutorServiceImpl.java @@ -33,9 +33,11 @@ public class ManagedExecutorServiceImpl extends AbstractExecutorService implemen private static final Logger LOGGER = Logger.getInstance(LogCategory.OPENEJB, ManagedExecutorServiceImpl.class); private final ExecutorService delegate; + private final ContextServiceImpl contextService; - public ManagedExecutorServiceImpl(final ExecutorService delegate) { + public ManagedExecutorServiceImpl(final ExecutorService delegate, final ContextServiceImpl contextService) { this.delegate = delegate; + this.contextService = contextService; } @Override @@ -121,7 +123,7 @@ public boolean awaitTermination(final long timeout, final TimeUnit unit) throws @Override public Future submit(final Callable task) { - final CUCallable wrapper = new CUCallable<>(task); + final CUCallable wrapper = new CUCallable<>(task, contextService); final Future future = delegate.submit(wrapper); wrapper.taskSubmitted(future, this, task); return new CUFuture<>(future, wrapper); @@ -129,7 +131,7 @@ public Future submit(final Callable task) { @Override public Future submit(final Runnable task, final T result) { - final CURunnable wrapper = new CURunnable(task); + final CURunnable wrapper = new CURunnable(task, contextService); final Future future = delegate.submit(wrapper, result); wrapper.taskSubmitted(future, this, task); return new CUFuture<>(future, wrapper); @@ -137,7 +139,7 @@ public Future submit(final Runnable task, final T result) { @Override public Future submit(final Runnable task) { - final CURunnable wrapper = new CURunnable(task); + final CURunnable wrapper = new CURunnable(task, contextService); final Future future = delegate.submit(wrapper); wrapper.taskSubmitted(future, this, task); return new CUFuture<>(Future.class.cast(future), wrapper); @@ -145,7 +147,7 @@ public Future submit(final Runnable task) { @Override public void execute(final Runnable command) { - final CURunnable wrapper = new CURunnable(command); + final CURunnable wrapper = new CURunnable(command, contextService); delegate.execute(wrapper); wrapper.taskSubmitted(null, this, command); } diff --git a/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ManagedScheduledExecutorServiceImpl.java b/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ManagedScheduledExecutorServiceImpl.java index e445ac93870..6ce434df0df 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ManagedScheduledExecutorServiceImpl.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/ManagedScheduledExecutorServiceImpl.java @@ -40,10 +40,12 @@ public class ManagedScheduledExecutorServiceImpl extends ManagedExecutorServiceImpl implements ManagedScheduledExecutorService { private final ScheduledExecutorService delegate; + private final ContextServiceImpl contextService; - public ManagedScheduledExecutorServiceImpl(final ScheduledExecutorService delegate) { - super(delegate); + public ManagedScheduledExecutorServiceImpl(final ScheduledExecutorService delegate, final ContextServiceImpl contextService) { + super(delegate, contextService); this.delegate = delegate; + this.contextService = contextService; } @@ -51,7 +53,7 @@ public ManagedScheduledExecutorServiceImpl(final ScheduledExecutorService delega public ScheduledFuture schedule(final Runnable runnable, final Trigger trigger) { final Date taskScheduledTime = new Date(); final AtomicReference> futureHandle = new AtomicReference<>(); - final TriggerRunnable wrapper = new TriggerRunnable(this, runnable, new CURunnable(runnable), trigger, taskScheduledTime, getTaskId(runnable), AtomicReference.class.cast(futureHandle)); + final TriggerRunnable wrapper = new TriggerRunnable(this, contextService, runnable, new CURunnable(runnable), trigger, taskScheduledTime, getTaskId(runnable), AtomicReference.class.cast(futureHandle)); final ScheduledFuture future = delegate.schedule(wrapper, trigger.getNextRunTime(wrapper.getLastExecution(), taskScheduledTime).getTime() - nowMs(), TimeUnit.MILLISECONDS); return initTriggerScheduledFuture(runnable, AtomicReference.class.cast(futureHandle), wrapper, ScheduledFuture.class.cast(future)); } @@ -60,7 +62,7 @@ public ScheduledFuture schedule(final Runnable runnable, final Trigger trigge public ScheduledFuture schedule(final Callable vCallable, final Trigger trigger) { final Date taskScheduledTime = new Date(); final AtomicReference> futureHandle = new AtomicReference<>(); - final TriggerCallable wrapper = new TriggerCallable<>(this, vCallable, new CUCallable<>(vCallable), trigger, taskScheduledTime, getTaskId(vCallable), futureHandle); + final TriggerCallable wrapper = new TriggerCallable<>(this, this.contextService, vCallable, new CUCallable<>(vCallable), trigger, taskScheduledTime, getTaskId(vCallable), futureHandle); final ScheduledFuture future = delegate.schedule(wrapper, trigger.getNextRunTime(wrapper.getLastExecution(), taskScheduledTime).getTime() - nowMs(), TimeUnit.MILLISECONDS); return initTriggerScheduledFuture(vCallable, futureHandle, wrapper, future); } diff --git a/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/SecurityThreadContextProvider.java b/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/SecurityThreadContextProvider.java new file mode 100755 index 00000000000..73105b04901 --- /dev/null +++ b/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/SecurityThreadContextProvider.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openejb.threads.impl; + +import jakarta.enterprise.concurrent.ContextServiceDefinition; +import jakarta.enterprise.concurrent.spi.ThreadContextProvider; +import jakarta.enterprise.concurrent.spi.ThreadContextRestorer; +import jakarta.enterprise.concurrent.spi.ThreadContextSnapshot; +import org.apache.openejb.BeanContext; +import org.apache.openejb.core.ThreadContext; +import org.apache.openejb.core.ivm.ClientSecurity; +import org.apache.openejb.core.security.AbstractSecurityService; +import org.apache.openejb.loader.SystemInstance; +import org.apache.openejb.spi.SecurityService; + +import javax.security.auth.login.LoginException; +import java.util.Map; + +public class SecurityThreadContextProvider implements ThreadContextProvider { + + private static final SecurityService SECURITY_SERVICE = SystemInstance.get().getComponent(SecurityService.class); + + @Override + public ThreadContextSnapshot currentContext(final Map props) { + boolean associate = false; + Object state = SECURITY_SERVICE.currentState(); + + if (state == null) { + state = ClientSecurity.getIdentity(); + associate = state != null; + } + + final Object securityServiceState = state; + final AbstractSecurityService.SecurityContext sc = getSecurityContext(); + + return new SecurityThreadContextSnapshot(associate, securityServiceState, sc); + } + + private AbstractSecurityService.SecurityContext getSecurityContext() { + final ThreadContext threadContext = ThreadContext.getThreadContext(); + + if (threadContext == null) { + return null; + } + + if (threadContext.getBeanContext() == null) { + return threadContext.get(AbstractSecurityService.SecurityContext.class); + } + + final BeanContext beanContext = threadContext.getBeanContext(); + if (beanContext.getRunAs() == null && beanContext.getRunAsUser() == null) { + return threadContext.get(AbstractSecurityService.SecurityContext.class); + } + + final AbstractSecurityService securityService = AbstractSecurityService.class.cast(SECURITY_SERVICE); + return new AbstractSecurityService.SecurityContext(securityService.getRunAsSubject(beanContext)); + } + + @Override + public ThreadContextSnapshot clearedContext(final Map props) { + return new SecurityThreadContextSnapshot(false, null, null); + } + + @Override + public String getThreadContextType() { + return ContextServiceDefinition.SECURITY; + } + + public class SecurityThreadContextSnapshot implements ThreadContextSnapshot { + + private final boolean associate; + private final Object securityServiceState; + private final AbstractSecurityService.SecurityContext sc; + + public SecurityThreadContextSnapshot(final boolean associate, final Object securityServiceState, final AbstractSecurityService.SecurityContext sc) { + this.associate = associate; + this.securityServiceState = securityServiceState; + this.sc = sc; + } + + @Override + public ThreadContextRestorer begin() { + final Object threadState; + + if (associate) { + try { + SECURITY_SERVICE.associate(securityServiceState); + } catch (final LoginException e) { + throw new IllegalStateException(e); + } + threadState = null; + } else { + threadState = SECURITY_SERVICE.currentState(); + SECURITY_SERVICE.setState(securityServiceState); + } + + final ThreadContext threadContext = ThreadContext.getThreadContext(); + final ThreadContext oldCtx; + if (threadContext != null) { + final ThreadContext newContext = new ThreadContext(threadContext); + oldCtx = ThreadContext.enter(newContext); + if (sc != null) { + newContext.set(AbstractSecurityService.SecurityContext.class, sc); + } + } else { + oldCtx = null; + } + + return new SecurityThreadContextRestorer(associate, oldCtx, threadState); + } + } + + public class SecurityThreadContextRestorer implements ThreadContextRestorer { + + private final boolean associate; + private final ThreadContext oldCtx; + private final Object threadState; + + public SecurityThreadContextRestorer(final boolean associate, final ThreadContext oldCtx, final Object threadState) { + this.associate = associate; + this.oldCtx = oldCtx; + this.threadState = threadState; + } + + @Override + public void endContext() throws IllegalStateException { + if (oldCtx != null) { + ThreadContext.exit(oldCtx); + } + + if (!associate) { + SECURITY_SERVICE.setState(threadState); + } else { + SECURITY_SERVICE.disassociate(); + } + } + } +} diff --git a/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/TxThreadContextProvider.java b/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/TxThreadContextProvider.java new file mode 100755 index 00000000000..a6eccdf0d12 --- /dev/null +++ b/container/openejb-core/src/main/java/org/apache/openejb/threads/impl/TxThreadContextProvider.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openejb.threads.impl; + +import jakarta.enterprise.concurrent.ContextServiceDefinition; +import jakarta.enterprise.concurrent.spi.ThreadContextProvider; +import jakarta.enterprise.concurrent.spi.ThreadContextRestorer; +import jakarta.enterprise.concurrent.spi.ThreadContextSnapshot; + +import java.util.Map; + +public class TxThreadContextProvider implements ThreadContextProvider { + @Override + public ThreadContextSnapshot currentContext(final Map props) { + return new TxThreadContextSnapshot(); + } + + @Override + public ThreadContextSnapshot clearedContext(final Map props) { + return new TxThreadContextSnapshot(); + } + + @Override + public String getThreadContextType() { + return ContextServiceDefinition.TRANSACTION; + } + + public class TxThreadContextSnapshot implements ThreadContextSnapshot { + + + public TxThreadContextSnapshot() { + } + + @Override + public ThreadContextRestorer begin() { + return new TxThreadContextRestorer(); + } + } + + public class TxThreadContextRestorer implements ThreadContextRestorer { + + public TxThreadContextRestorer() { + } + + @Override + public void endContext() throws IllegalStateException { + } + } +} diff --git a/container/openejb-core/src/main/java/org/apache/openejb/threads/task/CUCallable.java b/container/openejb-core/src/main/java/org/apache/openejb/threads/task/CUCallable.java index 2ea832a7ef7..4d4144a4fef 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/threads/task/CUCallable.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/threads/task/CUCallable.java @@ -16,13 +16,21 @@ */ package org.apache.openejb.threads.task; +import org.apache.openejb.threads.impl.ContextServiceImpl; +import org.apache.openejb.threads.impl.ContextServiceImplFactory; + import java.util.concurrent.Callable; public class CUCallable extends CUTask implements Callable { private final Callable delegate; public CUCallable(final Callable task) { - super(task); + super(task, ContextServiceImplFactory.newDefaultContextService()); + delegate = task; + } + + public CUCallable(final Callable task, final ContextServiceImpl contextService) { + super(task, contextService); delegate = task; } diff --git a/container/openejb-core/src/main/java/org/apache/openejb/threads/task/CURunnable.java b/container/openejb-core/src/main/java/org/apache/openejb/threads/task/CURunnable.java index 013460d25a2..a47b1bc836b 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/threads/task/CURunnable.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/threads/task/CURunnable.java @@ -16,13 +16,20 @@ */ package org.apache.openejb.threads.task; +import org.apache.openejb.threads.impl.ContextServiceImpl; +import org.apache.openejb.threads.impl.ContextServiceImplFactory; + import java.util.concurrent.Callable; public class CURunnable extends CUTask implements Runnable { private final Runnable delegate; public CURunnable(final Runnable task) { - super(task); + super(task, ContextServiceImplFactory.newPropagateEverythingContextService()); + delegate = task; + } + public CURunnable(final Runnable task, final ContextServiceImpl contextService) { + super(task, contextService); delegate = task; } diff --git a/container/openejb-core/src/main/java/org/apache/openejb/threads/task/CUTask.java b/container/openejb-core/src/main/java/org/apache/openejb/threads/task/CUTask.java index 5cdcdea8d94..53a7d9612c3 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/threads/task/CUTask.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/threads/task/CUTask.java @@ -17,22 +17,17 @@ package org.apache.openejb.threads.task; import org.apache.openejb.OpenEJBRuntimeException; -import org.apache.openejb.core.ThreadContext; -import org.apache.openejb.core.ivm.ClientSecurity; -import org.apache.openejb.core.security.AbstractSecurityService; import org.apache.openejb.loader.SystemInstance; import org.apache.openejb.spi.SecurityService; -import org.apache.openejb.util.Join; +import org.apache.openejb.threads.impl.ContextServiceImpl; import org.apache.openejb.util.LogCategory; import org.apache.openejb.util.Logger; -import javax.security.auth.login.LoginException; import java.util.ArrayList; import java.util.Collection; import java.util.concurrent.Callable; public abstract class CUTask extends ManagedTaskListenerTask implements Comparable { - // TODO: get rid of it as a static thing, make it owned by the executor probably private static final SecurityService SECURITY_SERVICE = SystemInstance.get().getComponent(SecurityService.class); // only updated in container startup phase, no concurrency possible, don't use it at runtime! @@ -47,31 +42,16 @@ public static void addContainerListener(final ContainerListener cl) { CONTAINER_LISTENERS = array; } - private final Context initialContext; + private final ContextServiceImpl contextService; + private final ContextServiceImpl.Snapshot snapshot; private final Object[] containerListenerStates; - public CUTask(final Object task) { + public CUTask(final Object task, final ContextServiceImpl contextService) { super(task); + this.contextService = contextService; + + snapshot = contextService.snapshot(null); - Object stateTmp = SECURITY_SERVICE.currentState(); - final boolean associate; - if (stateTmp == null) { - stateTmp = ClientSecurity.getIdentity(); - associate = stateTmp != null; - } else { - associate = false; - } - final ThreadContext threadContext = ThreadContext.getThreadContext(); - final AbstractSecurityService.SecurityContext sc = threadContext == null ? null : threadContext.get(AbstractSecurityService.SecurityContext.class); - if (threadContext != null && threadContext.getBeanContext() != null && - (threadContext.getBeanContext().getRunAs() != null || threadContext.getBeanContext().getRunAsUser() != null)) { - initialContext = new Context( - associate, stateTmp, - new AbstractSecurityService.SecurityContext(AbstractSecurityService.class.cast(SECURITY_SERVICE).getRunAsSubject(threadContext.getBeanContext())), - threadContext, Thread.currentThread().getContextClassLoader(), null); - } else { - initialContext = new Context(associate, stateTmp, sc, threadContext, Thread.currentThread().getContextClassLoader(), null); - } if (CONTAINER_LISTENERS.length > 0) { containerListenerStates = new Object[CONTAINER_LISTENERS.length]; for (int i = 0; i < CONTAINER_LISTENERS.length; i++) { @@ -83,7 +63,6 @@ public CUTask(final Object task) { } protected T invoke(final Callable call) throws Exception { - initialContext.enter(); final Object[] oldStates; if (CONTAINER_LISTENERS.length > 0) { oldStates = new Object[CONTAINER_LISTENERS.length]; @@ -94,6 +73,13 @@ protected T invoke(final Callable call) throws Exception { oldStates = null; } + ContextServiceImpl.State state = null; + + if (contextService != null && snapshot != null) { + state = contextService.enter(snapshot); + } + + Throwable throwable = null; try { taskStarting(future, executor, delegate); // do it in try to avoid issues if an exception is thrown @@ -111,7 +97,9 @@ protected T invoke(final Callable call) throws Exception { CONTAINER_LISTENERS[i].onEnd(oldStates[i]); } } - initialContext.exit(); + if (contextService != null && state != null) { + contextService.exit(state); + } } } } @@ -125,93 +113,24 @@ private T rethrow(final Throwable t) throws Exception { throw new OpenEJBRuntimeException(t.getMessage(), t); } + /* + * As the above is refactored to use ThreadContextProviders to align with the Jakarta EE 10 API, + * this is really just something that the TomEERealm can push exit tasks to the currently + * running Context. + */ public static final class Context { public static final ThreadLocal CURRENT = new ThreadLocal<>(); - /* - private static final Class[] THREAD_SCOPES = new Class[] { - RequestScoped.class, SessionScoped.class, ConversationScoped.class - }; - */ + private Context previous = null; - private final Object securityServiceState; - private final ThreadContext threadContext; - private final ClassLoader loader; - private final boolean associate; - private final AbstractSecurityService.SecurityContext securityContext; - private final Context stack; - - /* propagation of CDI context seems wrong - private final CdiAppContextsService contextService; - private final CdiAppContextsService.State cdiState; - */ - - private Context currentContext; private Collection exitTasks; - private Context(final boolean associate, final Object initialSecurityServiceState, - final AbstractSecurityService.SecurityContext securityContext, final ThreadContext initialThreadContext, - final ClassLoader initialLoader, final Context stack) { - this.associate = associate; - this.securityServiceState = initialSecurityServiceState; - this.securityContext = securityContext; - this.loader = initialLoader; - this.stack = stack; - // copy to ensure we have a thread safe data map - this.threadContext = initialThreadContext == null ? null : new ThreadContext(initialThreadContext); - - /* propagation of CDI context seems wrong - final ContextsService genericContextsService = WebBeansContext.currentInstance().getContextsService(); - if (CdiAppContextsService.class.isInstance(genericContextsService)) { - contextService = CdiAppContextsService.class.cast(genericContextsService); - cdiState = contextService.saveState(); - } else { - contextService = null; - cdiState = null; - } - */ - } - public void enter() { - final Thread thread = Thread.currentThread(); - - final ClassLoader oldCl = thread.getContextClassLoader(); - thread.setContextClassLoader(loader); - - final Object threadState; - if (associate) { - //noinspection unchecked - try { - SECURITY_SERVICE.associate(securityServiceState); - } catch (final LoginException e) { - throw new IllegalStateException(e); - } - threadState = null; - } else { - threadState = SECURITY_SERVICE.currentState(); - SECURITY_SERVICE.setState(securityServiceState); + if (previous != null) { + throw new IllegalStateException("Can't enter a context twice, create a new one, and call enter() on that."); } - final ThreadContext oldCtx; - if (threadContext != null) { // point A - final ThreadContext newContext = new ThreadContext(threadContext); - newContext.set(Context.class, this); - if (securityContext != null) { - newContext.set(AbstractSecurityService.ProvidedSecurityContext.class, new AbstractSecurityService.ProvidedSecurityContext(securityContext)); - } - oldCtx = ThreadContext.enter(newContext); - } else { - oldCtx = null; - } - - currentContext = new Context(associate, threadState, securityContext, oldCtx, oldCl, this); - - /* propagation of CDI context seems wrong - if (cdiState != null) { - contextService.restoreState(cdiState); - } - */ - + this.previous = CURRENT.get(); CURRENT.set(this); } @@ -234,53 +153,9 @@ public void exit() { } } - if (threadContext != null) { // ensure we use the same condition as point A, see OPENEJB-2109 - try { - ThreadContext.exit(currentContext.threadContext); - } catch (final RuntimeException re) { - if (errors == null) { - errors = new ArrayList<>(); - } - errors.add(re); - Logger.getInstance(LogCategory.OPENEJB, CUTask.class).warning(re.getMessage(), re); - } - } - try { - if (!associate) { - SECURITY_SERVICE.setState(currentContext.securityServiceState); - } else { - SECURITY_SERVICE.disassociate(); - } - } catch (final RuntimeException re) { - if (errors == null) { - errors = new ArrayList<>(); - } - errors.add(re); - Logger.getInstance(LogCategory.OPENEJB, CUTask.class).warning(re.getMessage(), re); - } - - /* propagation of CDI context seems wrong - if (currentContext.cdiState != null) { - contextService.restoreState(currentContext.cdiState); - contextService.removeThreadLocals(); - } - */ - - Thread.currentThread().setContextClassLoader(currentContext.loader); - if (currentContext.stack == null) { - CURRENT.remove(); - } else { - CURRENT.set(currentContext.stack); - } - currentContext = null; - - if (errors != null) { - if (errors.size() == 1) { - throw errors.iterator().next(); - } - throw new OpenEJBRuntimeException(Join.join("\n", Throwable::getMessage, errors)); - } + CURRENT.set(previous); + previous = null; } public void pushExitTask(final Runnable runnable) { diff --git a/container/openejb-core/src/main/java/org/apache/openejb/threads/task/TriggerCallable.java b/container/openejb-core/src/main/java/org/apache/openejb/threads/task/TriggerCallable.java index 7a8bc003d18..cf647dbc15a 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/threads/task/TriggerCallable.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/threads/task/TriggerCallable.java @@ -16,6 +16,7 @@ */ package org.apache.openejb.threads.task; +import org.apache.openejb.threads.impl.ContextServiceImpl; import org.apache.openejb.threads.impl.ManagedScheduledExecutorServiceImpl; import jakarta.enterprise.concurrent.Trigger; @@ -28,10 +29,11 @@ public class TriggerCallable extends TriggerTask implements Callable { private final Callable delegate; public TriggerCallable(final ManagedScheduledExecutorServiceImpl es, + final ContextServiceImpl contextService, final Callable original, final Callable wrap, final Trigger trigger, final Date taskScheduledTime, final String id, final AtomicReference> ref) { - super(original, es, trigger, taskScheduledTime, id, ref); + super(original, contextService, es, trigger, taskScheduledTime, id, ref); this.delegate = wrap; } diff --git a/container/openejb-core/src/main/java/org/apache/openejb/threads/task/TriggerRunnable.java b/container/openejb-core/src/main/java/org/apache/openejb/threads/task/TriggerRunnable.java index 993874e4a29..62d91ff42a2 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/threads/task/TriggerRunnable.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/threads/task/TriggerRunnable.java @@ -16,6 +16,7 @@ */ package org.apache.openejb.threads.task; +import org.apache.openejb.threads.impl.ContextServiceImpl; import org.apache.openejb.threads.impl.ManagedScheduledExecutorServiceImpl; import jakarta.enterprise.concurrent.Trigger; @@ -27,11 +28,12 @@ public class TriggerRunnable extends TriggerTask implements Runnable { private final Runnable delegate; public TriggerRunnable(final ManagedScheduledExecutorServiceImpl es, + final ContextServiceImpl contextService, final Runnable original, final Runnable runnable, final Trigger trigger, final Date taskScheduledTime, final String id, final AtomicReference> ref) { - super(original, es, trigger, taskScheduledTime, id, ref); + super(original, contextService, es, trigger, taskScheduledTime, id, ref); this.delegate = runnable; } diff --git a/container/openejb-core/src/main/java/org/apache/openejb/threads/task/TriggerTask.java b/container/openejb-core/src/main/java/org/apache/openejb/threads/task/TriggerTask.java index c276c1be083..be0afb5f17b 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/threads/task/TriggerTask.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/threads/task/TriggerTask.java @@ -16,6 +16,7 @@ */ package org.apache.openejb.threads.task; +import org.apache.openejb.threads.impl.ContextServiceImpl; import org.apache.openejb.threads.impl.ManagedScheduledExecutorServiceImpl; import jakarta.enterprise.concurrent.LastExecution; @@ -47,9 +48,9 @@ public abstract class TriggerTask extends CUTask { private final AtomicBoolean running = new AtomicBoolean(true); private volatile T result; - protected TriggerTask(final Object original, final ManagedScheduledExecutorServiceImpl es, final Trigger trigger, + protected TriggerTask(final Object original, final ContextServiceImpl contextService, final ManagedScheduledExecutorServiceImpl es, final Trigger trigger, final Date taskScheduledTime, final String id, final AtomicReference> ref) { - super(original); + super(original, contextService); this.executorService = es; this.trigger = trigger; this.scheduledTime = taskScheduledTime; diff --git a/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml b/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml index edfbc4648c4..ced5884db17 100644 --- a/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml +++ b/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml @@ -791,6 +791,13 @@ # Concurrency Utilities # ========================================================== --> + + SkipImplicitAttributes = true + Core = 5 ThreadFactory = org.apache.openejb.threads.impl.ManagedThreadFactoryImpl + Context = Default Context Service Lazy = true SkipImplicitAttributes = true @@ -822,11 +831,5 @@ Lazy = true SkipImplicitAttributes = true - - SkipImplicitAttributes = true - diff --git a/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/event/ResourceEventsTest.java b/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/event/ResourceEventsTest.java index bef9c43ae89..3624b01d25e 100644 --- a/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/event/ResourceEventsTest.java +++ b/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/event/ResourceEventsTest.java @@ -115,20 +115,24 @@ public static class Listener { private static final List EVENTS = new ArrayList<>(); public void onCreated(@Observes final ResourceCreated created) { - EVENTS.add(created); - if (created.is(Base.class) && "base".equals(created.getName())) { - created.replaceBy(new Child(created.getResource())); + if (created.resource instanceof Base) { + EVENTS.add(created); + if (created.is(Base.class) && "base".equals(created.getName())) { + created.replaceBy(new Child(created.getResource())); + } } } public void onDestroyed(@Observes final ResourceBeforeDestroyed destroyed) { - EVENTS.add(destroyed); - if (destroyed.is(Base.class) && destroyed.is(Child.class) && "base".equals(destroyed.getName())) { - final Object parent = Child.class.cast(destroyed.getResource()).parent; - try { - destroyed.replaceBy(new Assembler.ResourceInstance("base", parent, singleton(Base.class.getMethod("stop")), null)); - } catch (final NoSuchMethodException e) { - fail(e.getMessage()); + if (destroyed.name.equals("base") || destroyed.name.equals("base2")) { + EVENTS.add(destroyed); + if (destroyed.is(Base.class) && destroyed.is(Child.class) && "base".equals(destroyed.getName())) { + final Object parent = Child.class.cast(destroyed.getResource()).parent; + try { + destroyed.replaceBy(new Assembler.ResourceInstance("base", parent, singleton(Base.class.getMethod("stop")), null)); + } catch (final NoSuchMethodException e) { + fail(e.getMessage()); + } } } } diff --git a/container/openejb-core/src/test/java/org/apache/openejb/config/AutoConfigPersistenceUnitsTest.java b/container/openejb-core/src/test/java/org/apache/openejb/config/AutoConfigPersistenceUnitsTest.java index f8e57ab35ce..5cd5d26ae40 100644 --- a/container/openejb-core/src/test/java/org/apache/openejb/config/AutoConfigPersistenceUnitsTest.java +++ b/container/openejb-core/src/test/java/org/apache/openejb/config/AutoConfigPersistenceUnitsTest.java @@ -361,8 +361,8 @@ public void testFromWebAppIdJta() throws Exception { assembler.createApplication(appInfo); // Check results - final ResourceInfo generated = resources.get(1); - assertEquals(supplied.id + "NonJta", generated.id); + final ResourceInfo generated = findResource(supplied.id + "NonJta"); + assertNotNull(generated); assertEquals(supplied.service, generated.service); assertEquals(supplied.className, generated.className); assertEquals(supplied.properties.get("JdbcDriver"), generated.properties.get("JdbcDriver")); @@ -370,6 +370,16 @@ public void testFromWebAppIdJta() throws Exception { assertEquals("false", generated.properties.get("JtaManaged")); } + private ResourceInfo findResource(final String search) { + for (final ResourceInfo resource : resources) { + if (resource.id.equals(search)) { + return resource; + } + } + + return null; + } + /** * Existing data source "orange-id", non-jta managed * @@ -402,8 +412,8 @@ public void testFromWebAppIdNonJta() throws Exception { assembler.createApplication(appInfo); // Check results - final ResourceInfo generated = resources.get(1); - assertEquals(supplied.id + "Jta", generated.id); + final ResourceInfo generated = findResource(supplied.id + "Jta"); + assertNotNull(generated); assertEquals(supplied.service, generated.service); assertEquals(supplied.className, generated.className); assertEquals(supplied.properties.get("JdbcDriver"), generated.properties.get("JdbcDriver")); @@ -480,8 +490,8 @@ public void testFromWebAppContextJta() throws Exception { assembler.createApplication(appInfo); // Check results - final ResourceInfo generated = resources.get(1); - assertEquals(supplied.id + "NonJta", generated.id); + final ResourceInfo generated = findResource(supplied.id + "NonJta"); + assertNotNull(generated); assertEquals(supplied.service, generated.service); assertEquals(supplied.className, generated.className); assertEquals(supplied.properties.get("JdbcDriver"), generated.properties.get("JdbcDriver")); @@ -521,8 +531,8 @@ public void testFromWebAppContextNonJta() throws Exception { assembler.createApplication(appInfo); // Check results - final ResourceInfo generated = resources.get(1); - assertEquals(supplied.id + "Jta", generated.id); + final ResourceInfo generated = findResource(supplied.id + "Jta"); + assertNotNull(generated); assertEquals(supplied.service, generated.service); assertEquals(supplied.className, generated.className); assertEquals(supplied.properties.get("JdbcDriver"), generated.properties.get("JdbcDriver")); diff --git a/container/openejb-core/src/test/java/org/apache/openejb/threads/ManagedScheduledExecutorServiceTest.java b/container/openejb-core/src/test/java/org/apache/openejb/threads/ManagedScheduledExecutorServiceTest.java index 2ae985834c2..13d902f4674 100644 --- a/container/openejb-core/src/test/java/org/apache/openejb/threads/ManagedScheduledExecutorServiceTest.java +++ b/container/openejb-core/src/test/java/org/apache/openejb/threads/ManagedScheduledExecutorServiceTest.java @@ -20,6 +20,8 @@ import org.apache.openejb.loader.SystemInstance; import org.apache.openejb.ri.sp.PseudoSecurityService; import org.apache.openejb.spi.SecurityService; +import org.apache.openejb.threads.impl.ContextServiceImpl; +import org.apache.openejb.threads.impl.ContextServiceImplFactory; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -51,7 +53,8 @@ public static void reset() { @Test public void triggerCallableSchedule() throws Exception { - final ManagedScheduledExecutorService es = new ManagedScheduledExecutorServiceImplFactory().create(); + final ContextServiceImpl contextService = ContextServiceImplFactory.newDefaultContextService(); + final ManagedScheduledExecutorService es = new ManagedScheduledExecutorServiceImplFactory().create(contextService); final CountDownLatch counter = new CountDownLatch(5); final FutureAwareCallable callable = new FutureAwareCallable(counter); @@ -90,7 +93,8 @@ public boolean skipRun(final LastExecution lastExecutionInfo, final Date schedul @Test public void triggerRunnableSchedule() throws Exception { - final ManagedScheduledExecutorService es = new ManagedScheduledExecutorServiceImplFactory().create(); + final ContextServiceImpl contextService = ContextServiceImplFactory.newDefaultContextService(); + final ManagedScheduledExecutorService es = new ManagedScheduledExecutorServiceImplFactory().create(contextService); final CountDownLatch counter = new CountDownLatch(5); final FutureAwareCallable callable = new FutureAwareCallable(counter); @@ -128,7 +132,8 @@ public boolean skipRun(final LastExecution lastExecutionInfo, final Date schedul @Test public void simpleSchedule() throws Exception { - final ManagedScheduledExecutorService es = new ManagedScheduledExecutorServiceImplFactory().create(); + final ContextServiceImpl contextService = ContextServiceImplFactory.newDefaultContextService(); + final ManagedScheduledExecutorService es = new ManagedScheduledExecutorServiceImplFactory().create(contextService); final long start = System.currentTimeMillis(); final ScheduledFuture future = es.schedule(new Callable() { @Override diff --git a/container/openejb-jee/src/main/java/org/apache/openejb/jee/Application.java b/container/openejb-jee/src/main/java/org/apache/openejb/jee/Application.java index fbd5bdb624a..6fe76c61c20 100644 --- a/container/openejb-jee/src/main/java/org/apache/openejb/jee/Application.java +++ b/container/openejb-jee/src/main/java/org/apache/openejb/jee/Application.java @@ -138,6 +138,8 @@ public class Application implements JndiConsumer, NamedModule { protected KeyedCollection messageDestination; @XmlElement(name = "data-source") protected KeyedCollection dataSource; + @XmlElement(name = "context-service") + protected KeyedCollection contextService; @XmlElement(name = "jms-connection-factory", required = true) protected KeyedCollection jmsConnectionFactories; @XmlElement(name = "jms-destination") @@ -441,4 +443,12 @@ public Collection getJMSDestination() { public Map getJMSDestinationMap() { return KeyedCollection.class.cast(getJMSDestination()).toMap(); } + + @Override + public Map getContextServiceMap() { + if (contextService == null) { + contextService = new KeyedCollection(); + } + return this.contextService.toMap(); + } } diff --git a/container/openejb-jee/src/main/java/org/apache/openejb/jee/ApplicationClient.java b/container/openejb-jee/src/main/java/org/apache/openejb/jee/ApplicationClient.java index 432b3ef09d6..66afe3022ea 100644 --- a/container/openejb-jee/src/main/java/org/apache/openejb/jee/ApplicationClient.java +++ b/container/openejb-jee/src/main/java/org/apache/openejb/jee/ApplicationClient.java @@ -89,7 +89,8 @@ "messageDestination", "dataSource", "jmsConnectionFactories", - "jmsDestinations" + "jmsDestinations", + "contextService" }) public class ApplicationClient implements JndiConsumer, Lifecycle, NamedModule { @@ -149,6 +150,8 @@ public class ApplicationClient implements JndiConsumer, Lifecycle, NamedModule { @XmlTransient protected String mainClass; + @XmlElement(name="context-service") + private KeyedCollection contextService; public ApplicationClient() { } @@ -447,4 +450,12 @@ public Collection getJMSDestination() { public Map getJMSDestinationMap() { return KeyedCollection.class.cast(getJMSDestination()).toMap(); } + + @Override + public Map getContextServiceMap() { + if (contextService == null) { + contextService = new KeyedCollection(); + } + return this.contextService.toMap(); + } } diff --git a/container/openejb-jee/src/main/java/org/apache/openejb/jee/ContextService.java b/container/openejb-jee/src/main/java/org/apache/openejb/jee/ContextService.java new file mode 100755 index 00000000000..7bc9b10672a --- /dev/null +++ b/container/openejb-jee/src/main/java/org/apache/openejb/jee/ContextService.java @@ -0,0 +1,286 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openejb.jee; + +import java.util.ArrayList; +import java.util.List; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlID; +import jakarta.xml.bind.annotation.XmlSchemaType; +import jakarta.xml.bind.annotation.XmlType; +import jakarta.xml.bind.annotation.adapters.CollapsedStringAdapter; +import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import org.apache.openejb.jee.jba.JndiName; + + +/** + * + * + * Configuration of a ContextService. + * + * + * + *

Java class for context-serviceType complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType name="context-serviceType">
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element name="description" type="{http://java.sun.com/xml/ns/javaee}descriptionType" minOccurs="0"/>
+ *         <element name="name" type="{http://java.sun.com/xml/ns/javaee}jndi-nameType"/>
+ *         <element name="cleared" type="{http://java.sun.com/xml/ns/javaee}string" maxOccurs="unbounded" minOccurs="0"/>
+ *         <element name="propagated" type="{http://java.sun.com/xml/ns/javaee}string" maxOccurs="unbounded" minOccurs="0"/>
+ *         <element name="unchanged" type="{http://java.sun.com/xml/ns/javaee}string" maxOccurs="unbounded" minOccurs="0"/>
+ *         <element name="property" type="{http://java.sun.com/xml/ns/javaee}propertyType" maxOccurs="unbounded" minOccurs="0"/>
+ *       </sequence>
+ *       <attribute name="id" type="{http://www.w3.org/2001/XMLSchema}ID" />
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "context-serviceType", propOrder = { + "description", + "name", + "cleared", + "propagated", + "unchanged", + "property" +}) +public class ContextService implements Keyable{ + + @XmlElement + protected Description description; + @XmlElement(required = true) + protected JndiName name; + @XmlElement + protected List cleared; + @XmlElement + protected List propagated; + @XmlElement + protected List unchanged; + @XmlElement + protected List property; + @XmlAttribute(name = "id") + @XmlJavaTypeAdapter(CollapsedStringAdapter.class) + @XmlID + @XmlSchemaType(name = "ID") + protected java.lang.String id; + + /** + * Gets the value of the description property. + * + * @return + * possible object is + * {@link Description } + * + */ + public Description getDescription() { + return description; + } + + /** + * Sets the value of the description property. + * + * @param value + * allowed object is + * {@link Description } + * + */ + public void setDescription(Description value) { + this.description = value; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link JndiName } + * + */ + public JndiName getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link JndiName } + * + */ + public void setName(JndiName value) { + this.name = value; + } + + /** + * Gets the value of the cleared property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the Jakarta XML Binding object. + * This is why there is not a set method for the cleared property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getCleared().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link String } + * + * + */ + public List getCleared() { + if (cleared == null) { + cleared = new ArrayList(); + } + return this.cleared; + } + + /** + * Gets the value of the propagated property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the Jakarta XML Binding object. + * This is why there is not a set method for the propagated property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getPropagated().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link String } + * + * + */ + public List getPropagated() { + if (propagated == null) { + propagated = new ArrayList(); + } + return this.propagated; + } + + /** + * Gets the value of the unchanged property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the Jakarta XML Binding object. + * This is why there is not a set method for the unchanged property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getUnchanged().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link String } + * + * + */ + public List getUnchanged() { + if (unchanged == null) { + unchanged = new ArrayList(); + } + return this.unchanged; + } + + /** + * Gets the value of the property property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the Jakarta XML Binding object. + * This is why there is not a set method for the property property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getProperty().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link Property } + * + * + */ + public List getProperty() { + if (property == null) { + property = new ArrayList(); + } + return this.property; + } + + /** + * Gets the value of the id property. + * + * @return + * possible object is + * {@link java.lang.String } + * + */ + public java.lang.String getId() { + return id; + } + + /** + * Sets the value of the id property. + * + * @param value + * allowed object is + * {@link java.lang.String } + * + */ + public void setId(java.lang.String value) { + this.id = value; + } + + @Override + public String getKey() { + return this.getName().getvalue(); + } +} + diff --git a/container/openejb-jee/src/main/java/org/apache/openejb/jee/EntityBean.java b/container/openejb-jee/src/main/java/org/apache/openejb/jee/EntityBean.java index 742fb9f528b..da8d1c13c96 100644 --- a/container/openejb-jee/src/main/java/org/apache/openejb/jee/EntityBean.java +++ b/container/openejb-jee/src/main/java/org/apache/openejb/jee/EntityBean.java @@ -108,7 +108,8 @@ "jmsDestinations", "securityRoleRef", "securityIdentity", - "query" + "query", + "contextService" }) public class EntityBean implements RemoteBean { @@ -183,6 +184,8 @@ public class EntityBean implements RemoteBean { @XmlJavaTypeAdapter(CollapsedStringAdapter.class) @XmlID protected String id; + @XmlElement(name="context-service") + private KeyedCollection contextService; public EntityBean() { final Set publicIds = JaxbJavaee.currentPublicId.get(); @@ -626,4 +629,12 @@ public Collection getJMSDestination() { public Map getJMSDestinationMap() { return KeyedCollection.class.cast(getJMSDestination()).toMap(); } + + @Override + public Map getContextServiceMap() { + if (contextService == null) { + contextService = new KeyedCollection(); + } + return this.contextService.toMap(); + } } diff --git a/container/openejb-jee/src/main/java/org/apache/openejb/jee/Interceptor.java b/container/openejb-jee/src/main/java/org/apache/openejb/jee/Interceptor.java index 860524d5817..7c79e69a313 100644 --- a/container/openejb-jee/src/main/java/org/apache/openejb/jee/Interceptor.java +++ b/container/openejb-jee/src/main/java/org/apache/openejb/jee/Interceptor.java @@ -84,7 +84,8 @@ "prePassivate", "afterBegin", "beforeCompletion", - "afterCompletion" + "afterCompletion", + "contextService" }) public class Interceptor implements JndiConsumer, Session { @@ -140,6 +141,8 @@ public class Interceptor implements JndiConsumer, Session { @XmlJavaTypeAdapter(CollapsedStringAdapter.class) @XmlID protected String id; + @XmlElement(name="context-service") + private KeyedCollection contextService; public Interceptor() { } @@ -472,4 +475,12 @@ public Collection getJMSDestination() { public Map getJMSDestinationMap() { return KeyedCollection.class.cast(getJMSDestination()).toMap(); } + + @Override + public Map getContextServiceMap() { + if (contextService == null) { + contextService = new KeyedCollection(); + } + return this.contextService.toMap(); + } } diff --git a/container/openejb-jee/src/main/java/org/apache/openejb/jee/JndiConsumer.java b/container/openejb-jee/src/main/java/org/apache/openejb/jee/JndiConsumer.java index ce11cfebc57..d38c90d554c 100644 --- a/container/openejb-jee/src/main/java/org/apache/openejb/jee/JndiConsumer.java +++ b/container/openejb-jee/src/main/java/org/apache/openejb/jee/JndiConsumer.java @@ -78,4 +78,5 @@ public interface JndiConsumer { Collection getJMSDestination(); Map getJMSDestinationMap(); + Map getContextServiceMap(); } diff --git a/container/openejb-jee/src/main/java/org/apache/openejb/jee/MessageDrivenBean.java b/container/openejb-jee/src/main/java/org/apache/openejb/jee/MessageDrivenBean.java index 7825b8f07c5..4d0ec4e9b92 100644 --- a/container/openejb-jee/src/main/java/org/apache/openejb/jee/MessageDrivenBean.java +++ b/container/openejb-jee/src/main/java/org/apache/openejb/jee/MessageDrivenBean.java @@ -103,7 +103,8 @@ "jmsConnectionFactories", "jmsDestinations", "securityRoleRef", - "securityIdentity" + "securityIdentity", + "contextService" }) public class MessageDrivenBean implements EnterpriseBean, TimerConsumer, Invokable { @@ -173,6 +174,8 @@ public class MessageDrivenBean implements EnterpriseBean, TimerConsumer, Invokab @XmlJavaTypeAdapter(CollapsedStringAdapter.class) @XmlID protected String id; + @XmlElement(name="context-service") + private KeyedCollection contextService; public MessageDrivenBean() { } @@ -643,4 +646,12 @@ public void addAroundTimeout(final String method) { public String getTimerConsumerName() { return ejbName; } + + @Override + public Map getContextServiceMap() { + if (contextService == null) { + contextService = new KeyedCollection(); + } + return this.contextService.toMap(); + } } diff --git a/container/openejb-jee/src/main/java/org/apache/openejb/jee/SessionBean.java b/container/openejb-jee/src/main/java/org/apache/openejb/jee/SessionBean.java index 1e0c98c6ffa..24c5356fe5b 100644 --- a/container/openejb-jee/src/main/java/org/apache/openejb/jee/SessionBean.java +++ b/container/openejb-jee/src/main/java/org/apache/openejb/jee/SessionBean.java @@ -143,7 +143,8 @@ "prePassivate", "securityRoleRef", "securityIdentity", - "passivationCapable" + "passivationCapable", + "contextService" }) public class SessionBean implements RemoteBean, Session, TimerConsumer { @XmlTransient @@ -268,6 +269,8 @@ public class SessionBean implements RemoteBean, Session, TimerConsumer { @XmlTransient private final Collection parents = new ArrayList(); // always needed so initialize it early + @XmlElement(name="context-service") + private KeyedCollection contextService; public SessionBean() { } @@ -953,4 +956,12 @@ public Collection getJMSDestination() { public Map getJMSDestinationMap() { return KeyedCollection.class.cast(getJMSDestination()).toMap(); } + + @Override + public Map getContextServiceMap() { + if (contextService == null) { + contextService = new KeyedCollection(); + } + return this.contextService.toMap(); + } } diff --git a/container/openejb-jee/src/main/java/org/apache/openejb/jee/WebApp.java b/container/openejb-jee/src/main/java/org/apache/openejb/jee/WebApp.java index ec64f0c3df2..000b20f8868 100644 --- a/container/openejb-jee/src/main/java/org/apache/openejb/jee/WebApp.java +++ b/container/openejb-jee/src/main/java/org/apache/openejb/jee/WebApp.java @@ -98,7 +98,8 @@ "dataSource", "jmsConnectionFactories", "jmsDestinations", - "moduleName" + "moduleName", + "contextService" }) public class WebApp implements WebCommon, Lifecycle, NamedModule { @@ -193,6 +194,8 @@ public class WebApp implements WebCommon, Lifecycle, NamedModule { @XmlAttribute(required = true) @XmlJavaTypeAdapter(CollapsedStringAdapter.class) protected String version = "3.0"; + @XmlElement(name="context-service") + private KeyedCollection contextService; @Override public String getJndiConsumerName() { @@ -818,4 +821,12 @@ public Collection getJMSDestination() { public Map getJMSDestinationMap() { return KeyedCollection.class.cast(getJMSDestination()).toMap(); } + + @Override + public Map getContextServiceMap() { + if (contextService == null) { + contextService = new KeyedCollection(); + } + return this.contextService.toMap(); + } } diff --git a/container/openejb-jee/src/main/java/org/apache/openejb/jee/WebFragment.java b/container/openejb-jee/src/main/java/org/apache/openejb/jee/WebFragment.java index 1b344fee503..7d19c8a7e8a 100644 --- a/container/openejb-jee/src/main/java/org/apache/openejb/jee/WebFragment.java +++ b/container/openejb-jee/src/main/java/org/apache/openejb/jee/WebFragment.java @@ -95,7 +95,8 @@ "dataSource", "jmsConnectionFactories", "jmsDestinations", - "name" + "name", + "contextService" }) public class WebFragment implements WebCommon { @@ -188,6 +189,8 @@ public class WebFragment implements WebCommon { @XmlAttribute(required = true) @XmlJavaTypeAdapter(CollapsedStringAdapter.class) protected String version; + @XmlElement(name="context-service") + private KeyedCollection contextService; @Override @@ -635,4 +638,12 @@ public Collection getJMSDestination() { public Map getJMSDestinationMap() { return KeyedCollection.class.cast(getJMSDestination()).toMap(); } + + @Override + public Map getContextServiceMap() { + if (contextService == null) { + contextService = new KeyedCollection(); + } + return this.contextService.toMap(); + } } \ No newline at end of file diff --git a/tck/cdi-tomee/pom.xml b/tck/cdi-tomee/pom.xml index 4a60f9ea694..7e7101d3945 100644 --- a/tck/cdi-tomee/pom.xml +++ b/tck/cdi-tomee/pom.xml @@ -307,4 +307,4 @@ - + \ No newline at end of file diff --git a/tomee/pom.xml b/tomee/pom.xml index 6ce1ef27b64..08d6f20e6c4 100644 --- a/tomee/pom.xml +++ b/tomee/pom.xml @@ -374,4 +374,3 @@ - diff --git a/tomee/tomee-catalina/pom.xml b/tomee/tomee-catalina/pom.xml index 4fc493fd0c2..8c37dbdcf5c 100644 --- a/tomee/tomee-catalina/pom.xml +++ b/tomee/tomee-catalina/pom.xml @@ -137,4 +137,3 @@ - diff --git a/tomee/tomee-myfaces/src/main/java/org/apache/tomee/myfaces/TomEEMyFacesContainerInitializer.java b/tomee/tomee-myfaces/src/main/java/org/apache/tomee/myfaces/TomEEMyFacesContainerInitializer.java index 8f2fc8da756..8fd7b530964 100644 --- a/tomee/tomee-myfaces/src/main/java/org/apache/tomee/myfaces/TomEEMyFacesContainerInitializer.java +++ b/tomee/tomee-myfaces/src/main/java/org/apache/tomee/myfaces/TomEEMyFacesContainerInitializer.java @@ -23,10 +23,8 @@ import org.apache.catalina.core.ApplicationContextFacade; import org.apache.catalina.core.StandardContext; import org.apache.myfaces.webapp.MyFacesContainerInitializer; -import org.apache.myfaces.webapp.FacesInitializer; import org.apache.myfaces.webapp.StartupServletContextListener; import org.apache.openejb.loader.SystemInstance; -import org.apache.openejb.log.RemoveLogMessage; import org.apache.openejb.util.URLs; import jakarta.faces.webapp.FacesServlet; @@ -69,11 +67,11 @@ public void onStartup(final Set> classes, final ServletContext ctx) thr } // some message filtering, not a perf killer since this class don't log a lot - final Logger abstractInitializerLogger = Logger.getLogger(FacesInitializer.class.getName()); - abstractInitializerLogger.setFilter(new RemoveLogMessage( - new RemoveLogMessage(abstractInitializerLogger.getFilter(), - Level.WARNING, "No mappings of FacesServlet found. Abort initializing MyFaces."), - Level.WARNING, "No mappings of FacesServlet found. Abort destroy MyFaces.")); +// final Logger abstractInitializerLogger = Logger.getLogger(AbstractFacesInitializer.class.getName()); +// abstractInitializerLogger.setFilter(new RemoveLogMessage( +// new RemoveLogMessage(abstractInitializerLogger.getFilter(), +// Level.WARNING, "No mappings of FacesServlet found. Abort initializing MyFaces."), +// Level.WARNING, "No mappings of FacesServlet found. Abort destroy MyFaces.")); final boolean facesServletPresent = isFacesServletPresent(ctx); if (facesServletPresent || isFacesConfigPresent(ctx)) { @@ -135,7 +133,7 @@ private boolean isFacesServletPresent(final ServletContext ctx) { } private void addListener(final ServletContext ctx) { - final Logger logger = Logger.getLogger(FacesInitializer.class.getName()); + final Logger logger = Logger.getLogger(MyFacesContainerInitializer.class.getName()); logger.log(Level.INFO, "Installing " + StartupServletContextListener.class.getName() + ""); ctx.addListener(StartupServletContextListener.class); } @@ -193,4 +191,4 @@ private static Object get(final Class clazz, final Object facade) throws Exce field.setAccessible(acc); } } -} \ No newline at end of file +} diff --git a/tomee/tomee-plume-webapp/pom.xml b/tomee/tomee-plume-webapp/pom.xml index ac72f87a600..839d319cf1f 100644 --- a/tomee/tomee-plume-webapp/pom.xml +++ b/tomee/tomee-plume-webapp/pom.xml @@ -601,4 +601,3 @@ - diff --git a/tomee/tomee-plus-webapp/pom.xml b/tomee/tomee-plus-webapp/pom.xml index 42042a5294b..8b2f09d3448 100644 --- a/tomee/tomee-plus-webapp/pom.xml +++ b/tomee/tomee-plus-webapp/pom.xml @@ -612,4 +612,3 @@ - diff --git a/tomee/tomee-security/pom.xml b/tomee/tomee-security/pom.xml index 79c6e197a9f..d52b50ffd4b 100644 --- a/tomee/tomee-security/pom.xml +++ b/tomee/tomee-security/pom.xml @@ -109,4 +109,3 @@ -