Hi,
The PR indeed had the unfortunate wrong name at first, which was my mistake. Apologies again for that.
We can go ahead with porting the changes directly to GF. Easiest might be here to base that off of Omniconcurrent, and "just" copy that into GF with the correct license.
Both the initial PR (with the code copied/ported into GlassFish) as the second PR (with the code copied to a separate lib) work to a large degree, but did have some 18 failures still.
One was due to the jakarta concurrent api lib not being added to the JSP servlet's class path (in default-web.xml), so a .jsp using the jakarta.concurrent package would not compile. It's not really clear why Payara wouldn't need that, but perhaps there's an additional classpath used there now.
Another one seems to be due to a timing / ordering issue and seems like a bug in the Payara code. It concerns this one:
@Service
@Singleton
public class ConcurrentRuntime implements PostConstruct, PreDestroy {
private static ConcurrentRuntime _runtime;
public static ConcurrentRuntime getRuntime() {
if (_runtime == null) {
throw new RuntimeException("ConcurrentRuntime not initialized");
}
return _runtime;
}
private static void setRuntime(ConcurrentRuntime runtime) {
_runtime = runtime;
}
/**
* Constructor should be private to follow singleton pattern, but package access for unit testing.
*/
ConcurrentRuntime() {
setRuntime(this);
}
}
The static variable depends on ConcurrentRuntime "accidentally" being injected somewhere. If that happens, a later call to getRuntime() returns the instance. If by chance getRuntime() is called without any prior injection processed, there's an exception. When running the TCK in a certain order it throws the exception. When running the same tests in a slightly different order, it doesn't.
I quickfixed this by means of the following code. I guess that would be useful for Payara as well:
public static ConcurrentRuntime getRuntime() {
if (_runtime == null) {
_runtime = Globals.get(ConcurrentRuntime.class);
if (_runtime == null) {
throw new RuntimeException("ConcurrentRuntime not initialized");
}
}
return _runtime;
}
Another issue concerns the context service and this code:
private ContextServiceImpl prepareContextService(String contextServiceJndiName, String contextInfo, boolean contextInfoEnabled, boolean cleanupTransaction) {
ContextServiceImpl contextService = contextServiceMap.get(contextServiceJndiName);
if (contextService == null) {
// ...
contextService = createContextServiceImpl(contextServiceJndiName, contextInfoEnabled, propagated, cleared, Collections.EMPTY_SET);
contextServiceMap.put(contextServiceJndiName, contextService);
}
return contextService;
}
Here a default context service is created in a circumstance where a configured one should have been present and the following method should have been called:
public synchronized ContextServiceImpl createContextService(ResourceInfo resource, ContextServiceConfig config) {
ContextServiceImpl contextService = createContextServiceImpl(config.getJndiName(),
config.isContextInfoEnabledBoolean(), config.getPropagatedContexts(),
config.getClearedContexts(), config.getUchangedContexts());
return contextService;
}
In general the relation between how deployment descriptor parsing, annotation processing and JNDI entries work together is a little complicated. For that fact alone maybe some restructuring would be beneficial later, moving some of it to the Concurrency RI project for clarity. Some of it is also due to how JNDI based services work in Jakarta EE. Despite volumes of specifications, some of the basic APIs required have not been standardized, and perhaps the choice of making them JNDI based services/resources instead of CDI based wasn't ideal.
I'm attaching a quick 'n dirty drawing how some of the artefacts involves in GF/Payara relate:
It might be good to eventually have some of those responsibilities in the concurrent implementation project instead of as "glue code" in the server.
Kind regards,
Arjan Tijms