package org.jboss.as.clustering.infinispan.subsystem;
import static org.jboss.as.clustering.infinispan.InfinispanMessages.MESSAGES;
import static org.jboss.as.controller.PathAddress.pathAddress;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.scripting.ScriptingManager;
import org.infinispan.server.infinispan.SecurityActions;
import org.infinispan.server.infinispan.spi.service.CacheContainerServiceName;
import org.infinispan.tasks.Task;
import org.infinispan.tasks.TaskContext;
import org.infinispan.tasks.TaskExecution;
import org.infinispan.tasks.TaskManager;
import org.infinispan.topology.LocalTopologyManager;
import org.infinispan.util.logging.events.EventLog;
import org.infinispan.util.logging.events.EventLogCategory;
import org.infinispan.util.logging.events.EventLogLevel;
import org.infinispan.util.logging.events.EventLogManager;
import org.infinispan.util.logging.events.EventLogger;
import org.infinispan.xsite.GlobalXSiteAdminOperations;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.PathAddress;
import org.jboss.dmr.ModelNode;
import org.jboss.msc.service.ServiceController;
/**
* Custom commands related to the cache container.
*
* @author Pedro Ruivo
* @since 8.1
*/
public abstract class CacheContainerCommands implements OperationStepHandler {
final protected int pathOffset;
CacheContainerCommands(int pathOffset) {
this.pathOffset = pathOffset;
}
private static ModelNode toOperationResult(String s) {
ModelNode result = new ModelNode();
result.add(s);
return result;
}
/**
* An attribute write handler which performs special processing for ALIAS attributes.
*
* @param context the operation context
* @param operation the operation being executed
* @throws org.jboss.as.controller.OperationFailedException
*/
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
if (context.isNormalServer()) {
ModelNode operationResult;
try {
operationResult = invokeCommand(getEmbeddedCacheManager(context, operation), context, operation);
} catch (Exception e) {
throw new OperationFailedException(MESSAGES.failedToInvokeOperation(e.getLocalizedMessage()));
}
if (operationResult != null) {
context.getResult().set(operationResult);
}
}
}
protected abstract ModelNode invokeCommand(EmbeddedCacheManager cacheManager, OperationContext context, ModelNode operation) throws Exception;
private EmbeddedCacheManager getEmbeddedCacheManager(OperationContext context, ModelNode operation) {
final PathAddress address = pathAddress(operation.require(OP_ADDR));
final String cacheContainerName = address.getElement(address.size() - 1 - pathOffset).getValue();
final ServiceController<?> controller = context.getServiceRegistry(false)
.getService(CacheContainerServiceName.CACHE_CONTAINER.getServiceName(cacheContainerName));
return (EmbeddedCacheManager) controller.getValue();
}
public static class BackupTakeSiteOfflineCommand extends CacheContainerCommands {
public static final BackupTakeSiteOfflineCommand INSTANCE = new BackupTakeSiteOfflineCommand();
public BackupTakeSiteOfflineCommand() {
super(0);
}
@Override
protected ModelNode invokeCommand(EmbeddedCacheManager cacheManager, OperationContext context, ModelNode operation) throws Exception {
final String siteNameParameter = CacheContainerResource.SITE_NAME.getName();
final ModelNode siteName = operation.require(siteNameParameter);
GlobalXSiteAdminOperations xsiteAdminOperations = cacheManager.getGlobalComponentRegistry().getComponent(GlobalXSiteAdminOperations.class);
return toOperationResult(xsiteAdminOperations.takeSiteOffline(siteName.asString()));
}
}
public static class BackupBringSiteOnlineCommand extends CacheContainerCommands {
public static final BackupBringSiteOnlineCommand INSTANCE = new BackupBringSiteOnlineCommand();
public BackupBringSiteOnlineCommand() {
super(0);
}
@Override
protected ModelNode invokeCommand(EmbeddedCacheManager cacheManager, OperationContext context, ModelNode operation) throws Exception {
final String siteNameParameter = CacheContainerResource.SITE_NAME.getName();
final ModelNode siteName = operation.require(siteNameParameter);
GlobalXSiteAdminOperations xsiteAdminOperations = cacheManager.getGlobalComponentRegistry().getComponent(GlobalXSiteAdminOperations.class);
return toOperationResult(xsiteAdminOperations.bringSiteOnline(siteName.asString()));
}
}
public static class BackupPushStateCommand extends CacheContainerCommands {
public static final BackupPushStateCommand INSTANCE = new BackupPushStateCommand();
public BackupPushStateCommand() {
super(0);
}
@Override
protected ModelNode invokeCommand(EmbeddedCacheManager cacheManager, OperationContext context, ModelNode operation) throws Exception {
final String siteNameParameter = CacheContainerResource.SITE_NAME.getName();
final ModelNode siteName = operation.require(siteNameParameter);
GlobalXSiteAdminOperations xsiteAdminOperations = cacheManager.getGlobalComponentRegistry().getComponent(GlobalXSiteAdminOperations.class);
return toOperationResult(xsiteAdminOperations.pushState(siteName.asString()));
}
}
public static class BackupCancelPushStateCommand extends CacheContainerCommands {
public static final BackupCancelPushStateCommand INSTANCE = new BackupCancelPushStateCommand();
public BackupCancelPushStateCommand() {
super(0);
}
@Override
protected ModelNode invokeCommand(EmbeddedCacheManager cacheManager, OperationContext context, ModelNode operation) throws Exception {
final String siteNameParameter = CacheContainerResource.SITE_NAME.getName();
final ModelNode siteName = operation.require(siteNameParameter);
GlobalXSiteAdminOperations xsiteAdminOperations = cacheManager.getGlobalComponentRegistry().getComponent(GlobalXSiteAdminOperations.class);
return toOperationResult(xsiteAdminOperations.cancelPushState(siteName.asString()));
}
}
public static class ReadEventLogCommand extends CacheContainerCommands {
public static final ReadEventLogCommand INSTANCE = new ReadEventLogCommand();
public ReadEventLogCommand() {
super(0);
}
@Override
protected ModelNode invokeCommand(EmbeddedCacheManager cacheManager, OperationContext context, ModelNode operation) throws Exception {
int count = CacheContainerResource.COUNT.resolveModelAttribute(context, operation).asInt();
ModelNode sinceNode = CacheContainerResource.SINCE.resolveModelAttribute(context, operation);
Instant since = sinceNode.isDefined() ? ZonedDateTime.parse(sinceNode.asString(), DateTimeFormatter.ISO_DATE_TIME).toInstant() : Instant.now();
ModelNode categoryNode = CacheContainerResource.CATEGORY.resolveModelAttribute(context, operation);
Optional<EventLogCategory> category = categoryNode.isDefined() ? Optional.of(EventLogCategory.valueOf(categoryNode.asString())) : Optional.empty();
ModelNode levelNode = CacheContainerResource.LEVEL.resolveModelAttribute(context, operation);
Optional<EventLogLevel> level = levelNode.isDefined() ? Optional.of(EventLogLevel.valueOf(levelNode.asString())) : Optional.empty();
EventLogger eventLogger = EventLogManager.getEventLogger(cacheManager);
List<EventLog> events = eventLogger.getEvents(since, count, category, level);
final ModelNode result = new ModelNode().setEmptyList();
for (EventLog event : events) {
ModelNode node = result.addEmptyObject();
node.get("when").set(event.getWhen().toString());
node.get("level").set(event.getLevel().toString());
node.get("category").set(event.getCategory().toString());
node.get("message").set(event.getMessage());
event.getDetail().ifPresent(detail -> node.get("detail").set(detail));
event.getContext().ifPresent(ctx -> node.get("context").set(ctx));
event.getScope().ifPresent(scope -> node.get("scope").set(scope));
event.getWho().ifPresent(who -> node.get("who").set(who));
}
return result;
}
}
public static class TaskListCommand extends CacheContainerCommands {
public static final TaskListCommand INSTANCE = new TaskListCommand();
public TaskListCommand() {
super(0);
}
@Override
protected ModelNode invokeCommand(EmbeddedCacheManager cacheManager, OperationContext context, ModelNode operation) throws Exception {
TaskManager taskManager = cacheManager.getGlobalComponentRegistry().getComponent(TaskManager.class);
List<Task> tasks = taskManager.getTasks();
Collections.sort(tasks, (task1, task2) -> { return task1.getName().compareTo(task2.getName()); });
final ModelNode result = new ModelNode().setEmptyList();
for (Task task : tasks) {
ModelNode node = result.addEmptyObject();
node.get("name").set(task.getName());
node.get("type").set(task.getType());
node.get("mode").set(task.getExecutionMode().toString());
ModelNode parameters = node.get("parameters").setEmptyList();
task.getParameters().forEach(p -> parameters.add(new ModelNode().set(p)));
}
return result;
}
}
public static class TaskExecuteCommand extends CacheContainerCommands {
public static final TaskExecuteCommand INSTANCE = new TaskExecuteCommand();
public TaskExecuteCommand() {
super(0);
}
@Override
protected ModelNode invokeCommand(EmbeddedCacheManager cacheManager, OperationContext context, ModelNode operation) throws Exception {
String taskName = CacheContainerResource.TASK_NAME.resolveModelAttribute(context, operation).asString();
boolean taskAsync = CacheContainerResource.TASK_ASYNC.resolveModelAttribute(context, operation).asBoolean();
TaskManager taskManager = cacheManager.getGlobalComponentRegistry().getComponent(TaskManager.class);
TaskContext taskContext = new TaskContext();
ModelNode cacheNameNode = CacheContainerResource.TASK_CACHE_NAME.resolveModelAttribute(context, operation);
if (cacheNameNode.isDefined()) {
taskContext.cache(cacheManager.getCache(cacheNameNode.asString(), false));
}
ModelNode parameters = CacheContainerResource.TASK_PARAMETERS.resolveModelAttribute(context, operation);
if (parameters.isDefined()) {
parameters.asPropertyList().forEach(property -> taskContext.addParameter(property.getName(), property.getValue().asString()));
}
taskContext.logEvent(true);
CompletableFuture<Object> taskFuture = taskManager.runTask(taskName, taskContext);
if(taskAsync) {
return new ModelNode();
} else {
Object result = taskFuture.get();
return new ModelNode(String.valueOf(result));
}
}
}
public static class TaskStatusCommand extends CacheContainerCommands {
public static final TaskStatusCommand INSTANCE = new TaskStatusCommand();
public TaskStatusCommand() {
super(0);
}
@Override
protected ModelNode invokeCommand(EmbeddedCacheManager cacheManager, OperationContext context, ModelNode operation) throws Exception {
TaskManager taskManager = cacheManager.getGlobalComponentRegistry().getComponent(TaskManager.class);
List<TaskExecution> taskExecutions = taskManager.getCurrentTasks();
Collections.sort(taskExecutions, (task1, task2) -> { return task1.getStart().compareTo(task2.getStart()); });
final ModelNode result = new ModelNode().setEmptyList();
for (TaskExecution execution : taskExecutions) {
ModelNode node = result.addEmptyObject();
node.get("name").set(execution.getName());
node.get("start").set(execution.getStart().toString());
node.get("where").set(execution.getWhere());
execution.getWhat().ifPresent(what -> node.get("context").set(what));
execution.getWho().ifPresent(who -> node.get("who").set(who));
}
return result;
}
}
public static class ScriptAddCommand extends CacheContainerCommands {
public static final ScriptAddCommand INSTANCE = new ScriptAddCommand();
public ScriptAddCommand() {
super(0);
}
@Override
protected ModelNode invokeCommand(EmbeddedCacheManager cacheManager, OperationContext context, ModelNode operation) throws Exception {
ScriptingManager scriptManager = cacheManager.getGlobalComponentRegistry().getComponent(ScriptingManager.class);
String scriptName = CacheContainerResource.SCRIPT_NAME.resolveModelAttribute(context, operation).asString();
String scriptCode = CacheContainerResource.SCRIPT_CODE.resolveModelAttribute(context, operation).asString();
scriptManager.addScript(scriptName, scriptCode);
return null;
}
}
public static class ScriptCatCommand extends CacheContainerCommands {
public static final ScriptCatCommand INSTANCE = new ScriptCatCommand();
public ScriptCatCommand() {
super(0);
}
@Override
protected ModelNode invokeCommand(EmbeddedCacheManager cacheManager, OperationContext context, ModelNode operation) throws Exception {
ScriptingManager scriptManager = cacheManager.getGlobalComponentRegistry().getComponent(ScriptingManager.class);
String scriptName = CacheContainerResource.SCRIPT_NAME.resolveModelAttribute(context, operation).asString();
String scriptCode = scriptManager.getScript(scriptName);
return scriptCode != null ? new ModelNode().set(scriptCode) : null;
}
}
public static class ScriptRemoveCommand extends CacheContainerCommands {
public static final ScriptRemoveCommand INSTANCE = new ScriptRemoveCommand();
public ScriptRemoveCommand() {
super(0);
}
@Override
protected ModelNode invokeCommand(EmbeddedCacheManager cacheManager, OperationContext context, ModelNode operation) throws Exception {
ScriptingManager scriptManager = cacheManager.getGlobalComponentRegistry().getComponent(ScriptingManager.class);
String scriptName = CacheContainerResource.SCRIPT_NAME.resolveModelAttribute(context, operation).asString();
scriptManager.removeScript(scriptName);
return null;
}
}
public static class ClusterRebalanceCommand extends CacheContainerCommands {
public static final ClusterRebalanceCommand INSTANCE = new ClusterRebalanceCommand();
private ClusterRebalanceCommand() {
super(0);
}
@Override
protected ModelNode invokeCommand(EmbeddedCacheManager cacheManager, OperationContext context, ModelNode operation) throws Exception {
boolean value = CacheContainerResource.BOOL_VALUE.resolveModelAttribute(context, operation).asBoolean();
LocalTopologyManager topologyManager = SecurityActions.getGlobalComponentRegistry(cacheManager)
.getComponent(LocalTopologyManager.class);
if (topologyManager != null) {
topologyManager.setRebalancingEnabled(value);
}
return null;
}
}
}