-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add TCK tests for registering custom scopes/contexts with Build Compa…
…tible Extensions
- Loading branch information
Showing
21 changed files
with
574 additions
and
0 deletions.
There are no files selected for viewing
10 changes: 10 additions & 0 deletions
10
...n/java/org/jboss/cdi/tck/tests/build/compatible/extensions/customNormalScope/Command.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package org.jboss.cdi.tck.tests.build.compatible.extensions.customNormalScope; | ||
|
||
/** | ||
* A <em>command</em>. Should not be a bean. The {@link CommandExecutor CommandExecutor} | ||
* should be used to execute a command with automatic activation/deactivation of the command context. | ||
*/ | ||
@FunctionalInterface | ||
public interface Command { | ||
void execute(); | ||
} |
104 changes: 104 additions & 0 deletions
104
...org/jboss/cdi/tck/tests/build/compatible/extensions/customNormalScope/CommandContext.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package org.jboss.cdi.tck.tests.build.compatible.extensions.customNormalScope; | ||
|
||
import jakarta.enterprise.context.ContextNotActiveException; | ||
import jakarta.enterprise.context.spi.AlterableContext; | ||
import jakarta.enterprise.context.spi.Contextual; | ||
import jakarta.enterprise.context.spi.CreationalContext; | ||
|
||
import java.lang.annotation.Annotation; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
public class CommandContext implements AlterableContext { | ||
private final ThreadLocal<Map<Contextual<?>, ContextualInstance<?>>> currentContext = new ThreadLocal<>(); | ||
private final ThreadLocal<CommandExecution> currentCommandExecution = new ThreadLocal<>(); | ||
|
||
public Class<? extends Annotation> getScope() { | ||
return CommandScoped.class; | ||
} | ||
|
||
public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext) { | ||
Map<Contextual<?>, ContextualInstance<?>> store = currentContext.get(); | ||
|
||
if (store == null) { | ||
throw new ContextNotActiveException(); | ||
} | ||
|
||
ContextualInstance<T> instance = (ContextualInstance<T>) store.get(contextual); | ||
if (instance == null && creationalContext != null) { | ||
instance = new ContextualInstance<T>(contextual.create(creationalContext), creationalContext, contextual); | ||
store.put(contextual, instance); | ||
} | ||
return instance != null ? instance.get() : null; | ||
} | ||
|
||
public <T> T get(Contextual<T> contextual) { | ||
return get(contextual, null); | ||
} | ||
|
||
public boolean isActive() { | ||
return currentContext.get() != null; | ||
} | ||
|
||
public void destroy(Contextual<?> contextual) { | ||
Map<Contextual<?>, ContextualInstance<?>> ctx = currentContext.get(); | ||
if (ctx == null) { | ||
return; | ||
} | ||
ContextualInstance<?> contextualInstance = ctx.remove(contextual); | ||
if (contextualInstance != null) { | ||
contextualInstance.destroy(); | ||
} | ||
} | ||
|
||
void activate() { | ||
currentContext.set(new HashMap<>()); | ||
currentCommandExecution.set(new CommandExecution()); | ||
} | ||
|
||
void deactivate() { | ||
Map<Contextual<?>, ContextualInstance<?>> ctx = currentContext.get(); | ||
if (ctx == null) { | ||
return; | ||
} | ||
for (ContextualInstance<?> instance : ctx.values()) { | ||
try { | ||
instance.destroy(); | ||
} catch (Exception e) { | ||
System.err.println("Unable to destroy instance" + instance.get() + " for bean: " | ||
+ instance.getContextual()); | ||
} | ||
} | ||
ctx.clear(); | ||
currentContext.remove(); | ||
currentCommandExecution.remove(); | ||
} | ||
|
||
CommandExecution getCurrentCommandExecution() { | ||
return currentCommandExecution.get(); | ||
} | ||
|
||
static final class ContextualInstance<T> { | ||
private final T value; | ||
private final CreationalContext<T> creationalContext; | ||
private final Contextual<T> contextual; | ||
|
||
ContextualInstance(T instance, CreationalContext<T> creationalContext, Contextual<T> contextual) { | ||
this.value = instance; | ||
this.creationalContext = creationalContext; | ||
this.contextual = contextual; | ||
} | ||
|
||
T get() { | ||
return value; | ||
} | ||
|
||
Contextual<T> getContextual() { | ||
return contextual; | ||
} | ||
|
||
void destroy() { | ||
contextual.destroy(value, creationalContext); | ||
} | ||
} | ||
} |
49 changes: 49 additions & 0 deletions
49
...cdi/tck/tests/build/compatible/extensions/customNormalScope/CommandContextController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package org.jboss.cdi.tck.tests.build.compatible.extensions.customNormalScope; | ||
|
||
import jakarta.enterprise.context.ContextNotActiveException; | ||
import jakarta.enterprise.inject.spi.BeanContainer; | ||
|
||
import java.util.concurrent.atomic.AtomicBoolean; | ||
|
||
/** | ||
* Allows manual activation and deactivation of the {@linkplain CommandScoped command} context. | ||
* The {@code activate()} method returns {@code true} if the command context was not | ||
* active on the current thread at the moment of the call and hence was activated by the call. | ||
* When the command context was active on the current thread when {@code activate()} is called, | ||
* {@code false} is returned and the operation is otherwise a noop. | ||
* <p> | ||
* When {@code activate()} returns {@code true}, the caller is supposed to call | ||
* {@code deactivate()} later on. Calling {@code deactivate()} when the command context | ||
* is not active leads to {@code ContextNotActiveException}. Calling {@code deactivate()} | ||
* when the command context is active but was not activated by this controller is a noop. | ||
*/ | ||
public final class CommandContextController { | ||
private final CommandContext context; | ||
|
||
private final BeanContainer beanContainer; | ||
|
||
private final AtomicBoolean activated = new AtomicBoolean(false); | ||
|
||
CommandContextController(CommandContext context, BeanContainer beanContainer) { | ||
this.context = context; | ||
this.beanContainer = beanContainer; | ||
} | ||
|
||
public boolean activate() { | ||
try { | ||
beanContainer.getContext(CommandScoped.class); | ||
return false; | ||
} catch (ContextNotActiveException e) { | ||
context.activate(); | ||
activated.set(true); | ||
return true; | ||
} | ||
} | ||
|
||
public void deactivate() throws ContextNotActiveException { | ||
beanContainer.getContext(CommandScoped.class); | ||
if (activated.compareAndSet(true, false)) { | ||
context.deactivate(); | ||
} | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
.../tests/build/compatible/extensions/customNormalScope/CommandContextControllerCreator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package org.jboss.cdi.tck.tests.build.compatible.extensions.customNormalScope; | ||
|
||
import jakarta.enterprise.inject.Instance; | ||
import jakarta.enterprise.inject.build.compatible.spi.Parameters; | ||
import jakarta.enterprise.inject.build.compatible.spi.SyntheticBeanCreator; | ||
import jakarta.enterprise.inject.spi.BeanContainer; | ||
|
||
public class CommandContextControllerCreator implements SyntheticBeanCreator<CommandContextController> { | ||
@Override | ||
public CommandContextController create(Instance<Object> lookup, Parameters params) { | ||
BeanContainer beanContainer = lookup.select(BeanContainer.class).get(); | ||
CommandContext ctx = (CommandContext) beanContainer.getContexts(CommandScoped.class).iterator().next(); | ||
return new CommandContextController(ctx, beanContainer); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
...g/jboss/cdi/tck/tests/build/compatible/extensions/customNormalScope/CommandExecution.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package org.jboss.cdi.tck.tests.build.compatible.extensions.customNormalScope; | ||
|
||
import java.util.Date; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
public class CommandExecution { | ||
private final Date startedAt; | ||
|
||
private final Map<String, Object> data; | ||
|
||
CommandExecution() { | ||
this.startedAt = new Date(); | ||
this.data = new HashMap<>(); | ||
} | ||
|
||
Date getStartedAt() { | ||
return startedAt; | ||
} | ||
|
||
Map<String, Object> getData() { | ||
return data; | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
.../cdi/tck/tests/build/compatible/extensions/customNormalScope/CommandExecutionCreator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package org.jboss.cdi.tck.tests.build.compatible.extensions.customNormalScope; | ||
|
||
import jakarta.enterprise.inject.Instance; | ||
import jakarta.enterprise.inject.build.compatible.spi.Parameters; | ||
import jakarta.enterprise.inject.build.compatible.spi.SyntheticBeanCreator; | ||
import jakarta.enterprise.inject.spi.BeanContainer; | ||
|
||
public class CommandExecutionCreator implements SyntheticBeanCreator<CommandExecution> { | ||
@Override | ||
public CommandExecution create(Instance<Object> lookup, Parameters params) { | ||
CommandContext ctx = (CommandContext) lookup.select(BeanContainer.class).get().getContext(CommandScoped.class); | ||
return ctx.getCurrentCommandExecution(); | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
...rg/jboss/cdi/tck/tests/build/compatible/extensions/customNormalScope/CommandExecutor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package org.jboss.cdi.tck.tests.build.compatible.extensions.customNormalScope; | ||
|
||
import jakarta.enterprise.context.Dependent; | ||
import jakarta.inject.Inject; | ||
|
||
/** | ||
* Executes a {@link Command Command} in the command scope. That is, the command context | ||
* is activated before calling {@code Command.execute()} and deactivated when | ||
* {@code Command.execute()} returns (or throws). | ||
*/ | ||
@Dependent | ||
public class CommandExecutor { | ||
private final CommandContextController control; | ||
|
||
@Inject | ||
CommandExecutor(CommandContextController control) { | ||
this.control = control; | ||
} | ||
|
||
public void execute(Command command) { | ||
try { | ||
control.activate(); | ||
command.execute(); | ||
} finally { | ||
control.deactivate(); | ||
} | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
.../org/jboss/cdi/tck/tests/build/compatible/extensions/customNormalScope/CommandScoped.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package org.jboss.cdi.tck.tests.build.compatible.extensions.customNormalScope; | ||
|
||
import jakarta.enterprise.context.NormalScope; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Inherited; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
/** | ||
* Specifies that a bean belongs to the <em>command</em> normal scope. | ||
* <p> | ||
* A dependent-scoped bean of type {@link CommandContextController CommandContextController} | ||
* is provided that may be used to manually activate and deactivate the command context. | ||
* <p> | ||
* A dependent-scoped bean of type {@link CommandExecutor CommandExecutor} is provided that | ||
* may be used to execute a {@link Command Command} implementation, activating and deactivating | ||
* the command scope automatically. | ||
* <p> | ||
* A command-scoped bean of type {@link CommandExecution CommandExecution} is provided that contains | ||
* certain details about the command execution and allows exchanging data between beans in the same command scope. | ||
*/ | ||
@NormalScope | ||
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Inherited | ||
public @interface CommandScoped { | ||
} |
29 changes: 29 additions & 0 deletions
29
...i/tck/tests/build/compatible/extensions/customNormalScope/CustomNormalScopeExtension.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package org.jboss.cdi.tck.tests.build.compatible.extensions.customNormalScope; | ||
|
||
import jakarta.enterprise.context.Dependent; | ||
import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension; | ||
import jakarta.enterprise.inject.build.compatible.spi.Discovery; | ||
import jakarta.enterprise.inject.build.compatible.spi.MetaAnnotations; | ||
import jakarta.enterprise.inject.build.compatible.spi.ScannedClasses; | ||
import jakarta.enterprise.inject.build.compatible.spi.Synthesis; | ||
import jakarta.enterprise.inject.build.compatible.spi.SyntheticComponents; | ||
|
||
public class CustomNormalScopeExtension implements BuildCompatibleExtension { | ||
@Discovery | ||
public void discovery(MetaAnnotations meta, ScannedClasses scan) { | ||
meta.addContext(CommandScoped.class, CommandContext.class); | ||
} | ||
|
||
@Synthesis | ||
public void synthesis(SyntheticComponents syn) { | ||
syn.addBean(CommandContextController.class) | ||
.type(CommandContextController.class) | ||
.scope(Dependent.class) | ||
.createWith(CommandContextControllerCreator.class); | ||
|
||
syn.addBean(CommandExecution.class) | ||
.type(CommandExecution.class) | ||
.scope(CommandScoped.class) | ||
.createWith(CommandExecutionCreator.class); | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
...ss/cdi/tck/tests/build/compatible/extensions/customNormalScope/CustomNormalScopeTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package org.jboss.cdi.tck.tests.build.compatible.extensions.customNormalScope; | ||
|
||
import org.jboss.arquillian.container.test.api.Deployment; | ||
import org.jboss.cdi.tck.AbstractTest; | ||
import org.jboss.cdi.tck.cdi.Sections; | ||
import org.jboss.cdi.tck.shrinkwrap.WebArchiveBuilder; | ||
import org.jboss.shrinkwrap.api.spec.WebArchive; | ||
import org.jboss.test.audit.annotations.SpecAssertion; | ||
import org.testng.annotations.Test; | ||
|
||
import static org.testng.Assert.assertEquals; | ||
import static org.testng.Assert.assertNotNull; | ||
import static org.testng.Assert.assertTrue; | ||
|
||
// this test is basically a stripped down version of https://github.com/weld/command-context-example | ||
public class CustomNormalScopeTest extends AbstractTest { | ||
@Deployment | ||
public static WebArchive createTestArchive() { | ||
return new WebArchiveBuilder() | ||
.withTestClassPackage(CustomNormalScopeTest.class) | ||
.withBuildCompatibleExtension(CustomNormalScopeExtension.class) | ||
.build(); | ||
} | ||
|
||
@Test | ||
@SpecAssertion(section = Sections.DISCOVERY_PHASE, id = "a", note = "Register custom normal scope") | ||
public void commandContextController() { | ||
CommandContextController control = getContextualReference(CommandContextController.class); | ||
boolean activated = control.activate(); | ||
assertTrue(activated); | ||
try { | ||
assertEquals(getContextualReference(IdService.class).get(), getContextualReference(IdService.class).get()); | ||
} finally { | ||
control.deactivate(); | ||
} | ||
} | ||
|
||
@Test | ||
@SpecAssertion(section = Sections.DISCOVERY_PHASE, id = "a", note = "Register custom normal scope") | ||
public void commandExecutor() { | ||
CommandExecutor executor = getContextualReference(CommandExecutor.class); | ||
executor.execute(() -> { | ||
CommandExecution execution = getContextualReference(CommandExecution.class); | ||
IdService idService = getContextualReference(IdService.class); | ||
|
||
getContextualReference(MyService.class).process(); | ||
assertEquals(execution.getData().get("id"), idService.get()); | ||
assertNotNull(execution.getStartedAt()); | ||
}); | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
...java/org/jboss/cdi/tck/tests/build/compatible/extensions/customNormalScope/IdService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package org.jboss.cdi.tck.tests.build.compatible.extensions.customNormalScope; | ||
|
||
import java.util.UUID; | ||
|
||
@CommandScoped | ||
public class IdService { | ||
private final String id = UUID.randomUUID().toString(); | ||
|
||
public String get() { | ||
return id; | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
...java/org/jboss/cdi/tck/tests/build/compatible/extensions/customNormalScope/MyService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package org.jboss.cdi.tck.tests.build.compatible.extensions.customNormalScope; | ||
|
||
import jakarta.enterprise.context.ApplicationScoped; | ||
import jakarta.inject.Inject; | ||
|
||
@ApplicationScoped | ||
public class MyService { | ||
@Inject | ||
CommandExecution execution; | ||
|
||
@Inject | ||
IdService id; | ||
|
||
public void process() { | ||
execution.getData().put("id", id.get()); | ||
} | ||
} |
Oops, something went wrong.