Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
FORGE-848: Added @CommandScoped support
- Loading branch information
Showing
11 changed files
with
456 additions
and
18 deletions.
There are no files selected for viewing
38 changes: 38 additions & 0 deletions
38
shell-api/src/main/java/org/jboss/forge/shell/command/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,38 @@ | ||
/* | ||
* Copyright 2012 Red Hat, Inc. and/or its affiliates. | ||
* | ||
* Licensed under the Eclipse Public License version 1.0, available at | ||
* http://www.eclipse.org/legal/epl-v10.html | ||
*/ | ||
|
||
package org.jboss.forge.shell.command; | ||
|
||
import static java.lang.annotation.ElementType.FIELD; | ||
import static java.lang.annotation.ElementType.METHOD; | ||
import static java.lang.annotation.ElementType.TYPE; | ||
|
||
import java.lang.annotation.Documented; | ||
import java.lang.annotation.Inherited; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
import javax.enterprise.context.NormalScope; | ||
|
||
import org.jboss.forge.shell.plugins.Command; | ||
|
||
/** | ||
* Declares a bean as being scoped to the current {@link Command}. Beans using this scope will be destroyed when the | ||
* current {@link Command} finishes executing. The scope is active as long as the command is being executed. | ||
* | ||
* @author <a href="mailto:ggastald@redhat.com">George Gastaldi</a> | ||
*/ | ||
@NormalScope(passivating = false) | ||
@Inherited | ||
@Documented | ||
@Target({ TYPE, METHOD, FIELD }) | ||
@Retention(value = RetentionPolicy.RUNTIME) | ||
public @interface CommandScoped | ||
{ | ||
|
||
} |
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
71 changes: 71 additions & 0 deletions
71
shell-api/src/main/java/org/jboss/forge/shell/events/CommandVetoed.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,71 @@ | ||
/* | ||
* Copyright 2013 Red Hat, Inc. and/or its affiliates. | ||
* | ||
* Licensed under the Eclipse Public License version 1.0, available at | ||
* http://www.eclipse.org/legal/epl-v10.html | ||
*/ | ||
|
||
package org.jboss.forge.shell.events; | ||
|
||
import java.util.Map; | ||
|
||
import org.jboss.forge.shell.command.CommandMetadata; | ||
|
||
/** | ||
* Fired when a command is vetoed | ||
* | ||
* @author <a href="mailto:ggastald@redhat.com">George Gastaldi</a> | ||
* | ||
*/ | ||
public class CommandVetoed | ||
{ | ||
private CommandMetadata command; | ||
private Object[] parameters; | ||
private String originalStatement; | ||
private Map<Object, Object> context; | ||
|
||
public CommandVetoed(CommandMetadata command, Object[] parameters, String originalStatement, | ||
Map<Object, Object> context) | ||
{ | ||
super(); | ||
this.command = command; | ||
this.parameters = parameters; | ||
this.originalStatement = originalStatement; | ||
this.context = context; | ||
} | ||
|
||
public CommandMetadata getCommand() | ||
{ | ||
return command; | ||
} | ||
|
||
public void setCommand(CommandMetadata command) | ||
{ | ||
this.command = command; | ||
} | ||
|
||
public Object[] getParameters() | ||
{ | ||
return parameters; | ||
} | ||
|
||
public void setParameters(Object[] parameters) | ||
{ | ||
this.parameters = parameters; | ||
} | ||
|
||
public String getOriginalStatement() | ||
{ | ||
return originalStatement; | ||
} | ||
|
||
public void setOriginalStatement(String originalStatement) | ||
{ | ||
this.originalStatement = originalStatement; | ||
} | ||
|
||
public Map<Object, Object> getContext() | ||
{ | ||
return context; | ||
} | ||
} |
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
177 changes: 177 additions & 0 deletions
177
shell/src/main/java/org/jboss/forge/shell/command/CommandScopedContext.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,177 @@ | ||
/* | ||
* Copyright 2012 Red Hat, Inc. and/or its affiliates. | ||
* | ||
* Licensed under the Eclipse Public License version 1.0, available at | ||
* http://www.eclipse.org/legal/epl-v10.html | ||
*/ | ||
package org.jboss.forge.shell.command; | ||
|
||
import java.lang.annotation.Annotation; | ||
import java.util.Map; | ||
import java.util.Map.Entry; | ||
import java.util.Stack; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
import javax.enterprise.context.ContextNotActiveException; | ||
import javax.enterprise.context.spi.Context; | ||
import javax.enterprise.context.spi.Contextual; | ||
import javax.enterprise.context.spi.CreationalContext; | ||
import javax.enterprise.event.Observes; | ||
import javax.inject.Singleton; | ||
|
||
import org.jboss.forge.shell.events.CommandExecuted; | ||
import org.jboss.forge.shell.events.CommandVetoed; | ||
import org.jboss.forge.shell.events.PreCommandExecution; | ||
|
||
/** | ||
* This class provides lifecycle management for {@link CommandScoped} objects | ||
* | ||
* @author <a href="mailto:ggastald@redhat.com">George Gastaldi</a> | ||
*/ | ||
@Singleton | ||
public class CommandScopedContext implements Context | ||
{ | ||
private final static String COMPONENT_MAP_NAME = CommandScopedContext.class.getName() + ".componentInstanceMap"; | ||
private final static String CREATIONAL_MAP_NAME = CommandScopedContext.class.getName() + ".creationalInstanceMap"; | ||
private static final Stack<Map<Object, Object>> contextStack = new Stack<Map<Object, Object>>(); | ||
|
||
private void assertActive() | ||
{ | ||
if (!isActive()) | ||
{ | ||
throw new ContextNotActiveException( | ||
"Context with scope annotation @CommandScoped is not active since no command is in execution."); | ||
} | ||
} | ||
|
||
public Map<Object, Object> getCurrentContext() | ||
{ | ||
return contextStack.peek(); | ||
} | ||
|
||
public void create(@Observes final PreCommandExecution execution) | ||
{ | ||
contextStack.push(execution.getContext()); | ||
} | ||
|
||
public void destroy(@Observes final CommandExecuted event) | ||
{ | ||
destroyCurrentContext(); | ||
contextStack.pop(); | ||
} | ||
|
||
public void destroy(@Observes final CommandVetoed event) | ||
{ | ||
destroyCurrentContext(); | ||
contextStack.pop(); | ||
} | ||
|
||
@SuppressWarnings({ "rawtypes", "unchecked" }) | ||
private void destroyCurrentContext() | ||
{ | ||
Map<Contextual<?>, Object> componentInstanceMap = getComponentInstanceMap(); | ||
Map<Contextual<?>, CreationalContext<?>> creationalContextMap = getCreationalContextMap(); | ||
|
||
if ((componentInstanceMap != null) && (creationalContextMap != null)) | ||
{ | ||
for (Entry<Contextual<?>, Object> componentEntry : componentInstanceMap.entrySet()) | ||
{ | ||
Contextual contextual = componentEntry.getKey(); | ||
Object instance = componentEntry.getValue(); | ||
CreationalContext creational = creationalContextMap.get(contextual); | ||
|
||
contextual.destroy(instance, creational); | ||
} | ||
} | ||
getCurrentContext().clear(); | ||
} | ||
|
||
/* | ||
* Context Methods | ||
*/ | ||
|
||
@Override | ||
public boolean isActive() | ||
{ | ||
return !contextStack.isEmpty(); | ||
} | ||
|
||
@Override | ||
public Class<? extends Annotation> getScope() | ||
{ | ||
return CommandScoped.class; | ||
} | ||
|
||
@Override | ||
@SuppressWarnings("unchecked") | ||
public <T> T get(final Contextual<T> component) | ||
{ | ||
assertActive(); | ||
return (T) getComponentInstanceMap().get(component); | ||
} | ||
|
||
@Override | ||
@SuppressWarnings("unchecked") | ||
public <T> T get(final Contextual<T> component, final CreationalContext<T> creationalContext) | ||
{ | ||
assertActive(); | ||
|
||
T instance = get(component); | ||
|
||
if (instance == null) | ||
{ | ||
Map<Contextual<?>, CreationalContext<?>> creationalContextMap = getCreationalContextMap(); | ||
Map<Contextual<?>, Object> componentInstanceMap = getComponentInstanceMap(); | ||
|
||
synchronized (componentInstanceMap) | ||
{ | ||
instance = (T) componentInstanceMap.get(component); | ||
if (instance == null) | ||
{ | ||
instance = component.create(creationalContext); | ||
|
||
if (instance != null) | ||
{ | ||
componentInstanceMap.put(component, instance); | ||
creationalContextMap.put(component, creationalContext); | ||
} | ||
} | ||
} | ||
} | ||
|
||
return instance; | ||
} | ||
|
||
/* | ||
* Helpers for manipulating the Component/Context maps. | ||
*/ | ||
@SuppressWarnings("unchecked") | ||
private Map<Contextual<?>, Object> getComponentInstanceMap() | ||
{ | ||
ConcurrentHashMap<Contextual<?>, Object> map = (ConcurrentHashMap<Contextual<?>, Object>) getCurrentContext() | ||
.get(COMPONENT_MAP_NAME); | ||
|
||
if (map == null) | ||
{ | ||
map = new ConcurrentHashMap<Contextual<?>, Object>(); | ||
getCurrentContext().put(COMPONENT_MAP_NAME, map); | ||
} | ||
|
||
return map; | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
private Map<Contextual<?>, CreationalContext<?>> getCreationalContextMap() | ||
{ | ||
Map<Contextual<?>, CreationalContext<?>> map = (ConcurrentHashMap<Contextual<?>, CreationalContext<?>>) getCurrentContext() | ||
.get(CREATIONAL_MAP_NAME); | ||
|
||
if (map == null) | ||
{ | ||
map = new ConcurrentHashMap<Contextual<?>, CreationalContext<?>>(); | ||
getCurrentContext().put(CREATIONAL_MAP_NAME, map); | ||
} | ||
|
||
return map; | ||
} | ||
} |
Oops, something went wrong.