Skip to content

Commit

Permalink
FORGE-848: Added @CommandScoped support
Browse files Browse the repository at this point in the history
  • Loading branch information
gastaldi committed Jun 17, 2013
1 parent cbf85a0 commit d97f855
Show file tree
Hide file tree
Showing 11 changed files with 456 additions and 18 deletions.
@@ -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
{

}
Expand Up @@ -7,14 +7,16 @@

package org.jboss.forge.shell.events;

import java.util.Map;

import org.jboss.forge.shell.command.CommandMetadata;

/**
* Fired after a plugin/command has been executed and has finished processing.
*
*
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
* @author <a href="mailto:koen.aers@gmail.com">Koen Aers</a>
*
*
*/
public final class CommandExecuted
{
Expand All @@ -27,18 +29,16 @@ public enum Status
private CommandMetadata command;
private Object[] parameters;
private String originalStatement;

public CommandExecuted()
{
}
private Map<Object, Object> context;

public CommandExecuted(final Status status, final CommandMetadata command, final String originalStatement,
Object[] parameters)
Object[] parameters, Map<Object, Object> context)
{
this.status = status;
this.command = command;
this.originalStatement = originalStatement;
this.parameters = parameters;
this.context = context;
}

public Status getStatus()
Expand All @@ -60,4 +60,9 @@ public String getOriginalStatement()
{
return originalStatement;
}

public Map<Object, Object> getContext()
{
return context;
}
}
@@ -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;
}
}
Expand Up @@ -7,11 +7,13 @@

package org.jboss.forge.shell.events;

import java.util.Map;

import org.jboss.forge.shell.command.CommandMetadata;

/**
* Fired before a plugin/command is executed
*
*
* @author <a href="mailto:ggastald@redhat.com">George Gastaldi</a>
*/
public final class PreCommandExecution
Expand All @@ -20,17 +22,15 @@ public final class PreCommandExecution
private Object[] parameters;
private String originalStatement;
private boolean vetoed;

public PreCommandExecution()
{
}
private Map<Object, Object> context;

public PreCommandExecution(final CommandMetadata command, final String originalStatement,
Object[] parameters)
Object[] parameters, Map<Object, Object> context)
{
this.command = command;
this.originalStatement = originalStatement;
this.parameters = parameters;
this.context = context;
}

public CommandMetadata getCommand()
Expand Down Expand Up @@ -60,4 +60,9 @@ public void veto()
{
this.vetoed = true;
}

public Map<Object, Object> getContext()
{
return context;
}
}
@@ -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;
}
}

0 comments on commit d97f855

Please sign in to comment.