/*
* JBoss, Home of Professional Open Source.
* Copyright 2011, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.controller;
import java.io.InputStream;
import java.util.Set;
import org.jboss.as.controller.access.Action;
import org.jboss.as.controller.access.AuthorizationResult;
import org.jboss.as.controller.access.Caller;
import org.jboss.as.controller.access.Environment;
import org.jboss.as.controller.access.ResourceAuthorization;
import org.jboss.as.controller.capability.CapabilityServiceSupport;
import org.jboss.as.controller.capability.RuntimeCapability;
import org.jboss.as.controller.client.MessageSeverity;
import org.jboss.as.controller.notification.Notification;
import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.as.controller.registry.Resource;
import org.jboss.dmr.ModelNode;
import org.jboss.msc.service.ServiceController;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.ServiceRegistry;
import org.jboss.msc.service.ServiceTarget;
import org.wildfly.security.auth.server.SecurityIdentity;
/**
* The context for an operation step execution.
*
* @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
*/
public interface OperationContext extends ExpressionResolver {
/**
* Add an execution step to this operation process. Runtime operation steps are automatically added after
* configuration operation steps. Since only one operation may perform runtime work at a time, this method
* may block until other runtime operations have completed.
*
* @param step the step to add
* @param stage the stage at which the operation applies
* @throws IllegalArgumentException if the step handler is not valid for this controller type
*/
void addStep(OperationStepHandler step, Stage stage) throws IllegalArgumentException;
/**
* Add an execution step to this operation process. Runtime operation steps are automatically added after
* configuration operation steps. Since only one operation may perform runtime work at a time, this method
* may block until other runtime operations have completed.
*
* @param step the step to add
* @param stage the stage at which the operation applies
* @param addFirst add the handler before the others
* @throws IllegalArgumentException if the step handler is not valid for this controller type
*/
void addStep(OperationStepHandler step, Stage stage, boolean addFirst) throws IllegalArgumentException;
/**
* Add an execution step to this operation process, writing any output to the response object
* associated with the current step.
* Runtime operation steps are automatically added after configuration operation steps. Since only one operation
* may perform runtime work at a time, this method may block until other runtime operations have completed.
*
* @param operation the operation body to pass into the added step
* @param step the step to add
* @param stage the stage at which the operation applies
* @throws IllegalArgumentException if the step handler is not valid for this controller type
*/
void addStep(final ModelNode operation, final OperationStepHandler step, final Stage stage) throws IllegalArgumentException;
/**
* Add an execution step to this operation process, writing any output to the response object
* associated with the current step.
* Runtime operation steps are automatically added after configuration operation steps. Since only one operation
* may perform runtime work at a time, this method may block until other runtime operations have completed.
*
* @param operation the operation body to pass into the added step
* @param step the step to add
* @param stage the stage at which the operation applies
* @param addFirst add the handler before the others
* @throws IllegalArgumentException if the step handler is not valid for this controller type
*/
void addStep(final ModelNode operation, final OperationStepHandler step, final Stage stage, boolean addFirst) throws IllegalArgumentException;
/**
* Add an execution step to this operation process. Runtime operation steps are automatically added after
* configuration operation steps. Since only one operation may perform runtime work at a time, this method
* may block until other runtime operations have completed.
*
* @param response the response which the nested step should populate
* @param operation the operation body to pass into the added step
* @param step the step to add
* @param stage the stage at which the operation applies
* @throws IllegalArgumentException if the step handler is not valid for this controller type
*/
void addStep(ModelNode response, ModelNode operation, OperationStepHandler step, Stage stage) throws IllegalArgumentException;
/**
* Add an execution step to this operation process. Runtime operation steps are automatically added after
* configuration operation steps. Since only one operation may perform runtime work at a time, this method
* may block until other runtime operations have completed.
*
* @param response the response which the nested step should populate
* @param operation the operation body to pass into the added step
* @param step the step to add
* @param stage the stage at which the operation applies
* @param addFirst add the handler before the others
* @throws IllegalArgumentException if the step handler is not valid for this controller type
*/
void addStep(ModelNode response, ModelNode operation, OperationStepHandler step, Stage stage, boolean addFirst) throws IllegalArgumentException;
/**
* Add a {@link org.jboss.as.controller.OperationContext.Stage#MODEL} execution step to this operation process,
* including descriptive information for the operation.
* <p>
* This method is primarily intended for internal use.
* </p>
*
* @param stepDefinition the definition of the step to add
* @param stepHandler the handler for the step
* @param addFirst add the handler before the others @throws IllegalArgumentException if the step handler is not valid for this controller type
* @throws java.lang.IllegalStateException if {@link #getCurrentStage() the current stage} is not {@link Stage#MODEL}
*/
void addModelStep(OperationDefinition stepDefinition, OperationStepHandler stepHandler, boolean addFirst) throws IllegalArgumentException;
/**
* Add a {@link org.jboss.as.controller.OperationContext.Stage#MODEL} execution step to this operation process,
* including descriptive information for the operation.
* <p>
* This method is primarily intended for use by a handler for the {@code composite} operation.
* </p>
*
* @param response the response which the nested step should populate
* @param operation the operation body to pass into the added step
* @param stepDefinition the definition of the step to add
* @param stepHandler the handler for the step
* @param addFirst add the handler before the others @throws IllegalArgumentException if the step handler is not valid for this controller type
* @throws java.lang.IllegalStateException if {@link #getCurrentStage() the current stage} is not {@link Stage#MODEL}
*/
void addModelStep(ModelNode response, ModelNode operation, OperationDefinition stepDefinition, OperationStepHandler stepHandler, boolean addFirst) throws IllegalArgumentException;
/**
* Get a stream which is attached to the request.
*
* @param index the index
* @return the input stream
*/
InputStream getAttachmentStream(int index);
/**
* Gets the number of streams attached to the request.
*
* @return the number of streams
*/
int getAttachmentStreamCount();
/**
* Get the node into which the operation result should be written.
*
* @return the result node
*/
ModelNode getResult();
/**
* Returns whether {@link #getResult()} has been invoked.
*
* @return {@code true} if {@link #getResult()} has been invoked
*/
boolean hasResult();
/**
* Attach a stream to be included as part of the response. The return value of this method should be
* used as the value of the {@link #getResult() result} for the step that invokes this method. Callers
* can then use that value to find the stream in the
* {@link org.jboss.as.controller.client.OperationResponse response} to this operation.
*
* @param mimeType the mime type of the stream. Cannot be {@code null}
* @param stream the stream. Cannot be {@code null}
* @return a uuid for the stream. Will not be {@code null}
*
* @throws IllegalStateException if {@link #isBooting()} returns {@code true}.
*/
String attachResultStream(String mimeType, InputStream stream);
/**
* Attach a stream to be included as part of the response, with a predetermined UUID.
* <p>
* This method is intended for use by core handlers related to managed domain operation
* as they propagate a stream throughout a domain. Ordinary handlers should use
* {@link #attachResultStream(String, java.io.InputStream)}.
*
* @param mimeType the mime type of the stream. Cannot be {@code null}
* @param stream the stream. Cannot be {@code null}
*
* @throws IllegalStateException if {@link #isBooting()} returns {@code true}.
*/
void attachResultStream(String uuid, String mimeType, InputStream stream);
/**
* Get the failure description response node, creating it if necessary.
*
* @return the failure description
*/
ModelNode getFailureDescription();
/**
* Returns whether {@link #getFailureDescription()} has been invoked.
*
* @return {@code true} if {@link #getFailureDescription()} has been invoked
*/
boolean hasFailureDescription();
/**
* Get the node into which the details of server results in a multi-server managed domain operation should be written.
*
* @return the server results node
*
* @throws IllegalStateException if this process is not a {@link ProcessType#HOST_CONTROLLER}
*/
ModelNode getServerResults();
/**
* Get the response-headers response node, creating it if necessary. Ordinary operation step handlers should not
* use this API for manipulating the {@code operation-requires-restart} or {@code process-state} headers. Use
* {@link #reloadRequired()} and {@link #restartRequired()} for that. (Some core operation step handlers used
* for coordinating execution of operations across different processes in a managed domain may use this
* method to manipulate the {@code operation-requires-restart} or {@code process-state} headers, but that is
* an exception.)
*
* @return the response-headers node
*/
ModelNode getResponseHeaders();
/**
* Complete a step, while registering for
* {@link RollbackHandler#handleRollback(OperationContext, ModelNode) a notification} if the work done by the
* caller needs to be rolled back}.
*
* @param rollbackHandler the handler for any rollback notification. Cannot be {@code null}.
*/
void completeStep(RollbackHandler rollbackHandler);
/**
* Complete a step, while registering for
* {@link ResultHandler#handleResult(ResultAction, OperationContext, ModelNode) a notification} when the overall
* result of the operation is known. Handlers that register for notifications will receive the notifications in
* the reverse of the order in which their steps execute.
*
* @param resultHandler the handler for the result notification. Cannot be {@code null}.
*/
void completeStep(ResultHandler resultHandler);
/**
* Called by an {@link OperationStepHandler} to indicate it has completed its handling of a step and is
* uninterested in the result of subsequent steps. This is a convenience method that is equivalent to a call to
* {@link #completeStep(ResultHandler) completeStep(OperationContext.ResultHandler.NO_OP_RESULT_HANDLER)}.
* <p>
* A common user of this method would be a {@link Stage#MODEL} handler. Typically such a handler would not need
* to take any further action in the case of a {@link ResultAction#KEEP successful result} for the overall operation.
* If the operation result is a {@link ResultAction#ROLLBACK rollback}, the {@code OperationContext} itself
* will ensure any changes made to the model are discarded, again requiring no action on the part of the handler.
* So a {@link Stage#MODEL} handler typically can be uninterested in the result of the overall operation and can
* use this method.
* </p>
*
* @deprecated invoking this method is unnecessary since if an {@code OperationStepHandler} does not call one of
* the {@link #completeStep(org.jboss.as.controller.OperationContext.ResultHandler) variants} from its
* {@code execute} method, a no-op {@code ResultHandler} will automatically be registered
*/
@Deprecated
void stepCompleted();
/**
* Get the type of process in which this operation is executing.
*
* @return the process type. Will not be {@code null}
*/
ProcessType getProcessType();
/**
* Gets the running mode of the process.
*
* @return the running mode. Will not be {@code null}
*/
RunningMode getRunningMode();
/**
* Determine whether the controller is currently performing boot tasks.
*
* @return whether the controller is currently booting
*/
boolean isBooting();
/**
* Convenience method to check if the {@link #getProcessType() process type} is {@link ProcessType#isServer() a server type}
* and the {@link #getRunningMode() running mode} is {@link RunningMode#NORMAL}. The typical usage would
* be for handlers that are only meant to execute on a normally running server, not on a host controller
* or on a {@link RunningMode#ADMIN_ONLY} server.
*
* @return {@code true} if the {@link #getProcessType() process type} is {@link ProcessType#isServer() a server type}
* and the {@link #getRunningMode() running mode} is {@link RunningMode#NORMAL}.
*/
boolean isNormalServer();
/**
* Determine whether the current operation is bound to be rolled back.
*
* @return {@code true} if the operation will be rolled back
*/
boolean isRollbackOnly();
/**
* Indicate that the operation should be rolled back, regardless of outcome.
*/
void setRollbackOnly();
/**
* Gets whether any changes made by the operation should be rolled back if an error is introduced
* by a {@link Stage#RUNTIME} or {@link Stage#VERIFY} handler.
*
* @return {@code true} if the operation should rollback if there is a runtime stage failure
*/
boolean isRollbackOnRuntimeFailure();
/**
* Gets whether {@link Stage#RUNTIME} handlers can restart (or remove) runtime services in order to
* make the operation take effect. If {@code false} and the operation cannot be effected without restarting
* or removing services, the handler should invoke {@link #reloadRequired()} or {@link #restartRequired()}.
*
* @return {@code true} if a service restart or removal is allowed
*/
boolean isResourceServiceRestartAllowed();
/**
* Notify the context that the process requires a stop and re-start of its root service (but not a full process
* restart) in order to ensure stable operation and/or to bring its running state in line with its persistent configuration.
*
* @see ControlledProcessState.State#RELOAD_REQUIRED
*/
void reloadRequired();
/**
* Notify the context that the process must be terminated and replaced with a new process in order to ensure stable
* operation and/or to bring the running state in line with the persistent configuration.
*
* @see ControlledProcessState.State#RESTART_REQUIRED
*/
void restartRequired();
/**
* Notify the context that a previous call to {@link #reloadRequired()} can be ignored (typically because the change
* that led to the need for reload has been rolled back.)
*/
void revertReloadRequired();
/**
* Notify the context that a previous call to {@link #restartRequired()} can be ignored (typically because the change
* that led to the need for restart has been rolled back.)
*/
void revertRestartRequired();
/**
* Notify the context that an update to the runtime that would normally have been made could not be made due to
* the current state of the process. As an example, a step handler that can only update the runtime when
* {@link #isBooting()} is {@code true} must invoke this method if it is executed when {@link #isBooting()}
* is {@code false}.
*/
void runtimeUpdateSkipped();
/**
* Gets the address associated with the currently executing step.
* @return the address. Will not be {@code null}
*/
PathAddress getCurrentAddress();
/**
* Gets the {@link PathElement#getValue() value} of the {@link #getCurrentAddress() current address'}
* {@link PathAddress#getLastElement() last element}.
*
* @return the last element value
*
* @throws java.lang.IllegalStateException if {@link #getCurrentAddress()} is the empty address
*/
String getCurrentAddressValue();
/**
* Get a read only view of the managed resource registration. The registration is relative to the operation address.
*
* @return the model node registration
*/
ImmutableManagementResourceRegistration getResourceRegistration();
/**
* Get a mutable view of the managed resource registration. The registration is relative to the operation address.
*
* @return the model node registration
*/
ManagementResourceRegistration getResourceRegistrationForUpdate();
/**
* Get a read only view of the root managed resource registration.
*
* @return the root resource registration
*/
ImmutableManagementResourceRegistration getRootResourceRegistration();
/**
* Get the service registry. If the step is not a runtime operation handler step, an exception will be thrown. The
* returned registry must not be used to remove services, if an attempt is made to call {@code ServiceController.setMode(REMOVE)}
* on a {@code ServiceController} returned from this registry an {@code IllegalStateException} will be thrown. To
* remove a service call {@link #removeService(org.jboss.msc.service.ServiceName)}.
*
* @param modify {@code true} if the operation may be modifying a service, {@code false} otherwise
* @return the service registry
* @throws UnsupportedOperationException if the calling step is not a runtime operation step
*/
ServiceRegistry getServiceRegistry(boolean modify) throws UnsupportedOperationException;
/**
* Initiate a service removal. If the step is not a runtime operation handler step, an exception will be thrown. Any
* subsequent step which attempts to add a service with the same name will block until the service removal completes.
* The returned controller may be used to attempt to cancel a removal in progress.
*
* @param name the service to remove
* @return the controller of the service to be removed if service of given name exists; null otherwise
* @throws UnsupportedOperationException if the calling step is not a runtime operation step
*/
ServiceController<?> removeService(ServiceName name) throws UnsupportedOperationException;
/**
* Initiate a service removal. If the step is not a runtime operation handler step, an exception will be thrown. Any
* subsequent step which attempts to add a service with the same name will block until the service removal completes.
*
* @param controller the service controller to remove
* @throws UnsupportedOperationException if the calling step is not a runtime operation step
*/
void removeService(ServiceController<?> controller) throws UnsupportedOperationException;
/**
* Get the service target. If the step is not a runtime operation handler step, an exception will be thrown. The
* returned service target is limited such that only the service add methods are supported. If a service added
* to this target was removed by a prior operation step, the install will wait until the removal completes.
*
* @return the service target
* @throws UnsupportedOperationException if the calling step is not a runtime operation step
*/
ServiceTarget getServiceTarget() throws UnsupportedOperationException;
/**
* Acquire the controlling {@link ModelController}'s exclusive lock. Holding this lock prevent other operations
* from mutating the model, the {@link ManagementResourceRegistration management resource registry} or the runtime
* service registry until the lock is released. The lock is automatically released when the
* {@link OperationStepHandler#execute(OperationContext, org.jboss.dmr.ModelNode) execute method} of the handler
* that invoked this method returns.
* <p>
* This method should be little used. The model controller's exclusive lock is acquired automatically when any
* of the operations in this interface that imply mutating the model, management resource registry or service
* registry are invoked. The only use for this method are special situations where an exclusive lock is needed
* but none of those methods will be invoked.
* </p>
*/
void acquireControllerLock();
/**
* Create a new resource, relative to the executed operation address. Since only one operation
* may write at a time, this operation may block until other writing operations have completed.
*
* @param address the (possibly empty) address where the resource should be created. Address is relative to the
* address of the operation being executed
* @return the created resource
* @throws IllegalStateException if a resource already exists at the given address
* @throws UnsupportedOperationException if the calling operation is not a model operation
*/
Resource createResource(PathAddress address) throws UnsupportedOperationException;
/**
* Add a new resource, at the executed operation address. Since only one operation
* may write at a time, this operation may block until other writing operations have completed.
*
* @param address the (possibly empty) address where the resource should be added. Address is relative to the
* address of the operation being executed
* @param toAdd the new resource
* @throws IllegalStateException if a resource already exists at the given address, or if the resource does not support ordered childred
* @throws UnsupportedOperationException if the calling operation is not a model operation
*/
void addResource(PathAddress address, Resource toAdd);
/**
* Add a new resource, at to the executed operation address. Since only one operation
* may write at a time, this operation may block until other writing operations have completed.
*
* @param address the (possibly empty) address where the resource should be added. Address is relative to the
* address of the operation being executed
* @param index the index of the resource to be created in the parent resources children of this type
* @param toAdd the new resource
* @throws IllegalStateException if a resource already exists at the given address
* @throws UnsupportedOperationException if the calling operation is not a model operation
*/
void addResource(PathAddress address, int index, Resource toAdd);
/**
* Get the resource for read only operations, relative to the executed operation address. Reads never block.
* If a write action was previously performed, the value read will be from an uncommitted copy of the the management model.<br/>
*
* Note: By default the returned resource is read-only copy of the entire sub-model. In case this is not required use
* {@link OperationContext#readResource(PathAddress, boolean)} instead.
*
* @param relativeAddress the (possibly empty) address where the resource should be added. The address is relative to the
* address of the operation being executed
* @return the resource
*/
Resource readResource(PathAddress relativeAddress);
/**
* Get the resource for read only operations, relative to the executed operation address. Reads never block.
* If a write action was previously performed, the value read will be from an uncommitted copy of the the management model.
*
* @param relativeAddress the (possibly empty) address where the resource should be added. The address is relative to the
* address of the operation being executed
* @param recursive whether the model should be read recursively or not
* @return the resource
*/
Resource readResource(PathAddress relativeAddress, boolean recursive);
/**
* Read an addressable resource from the root of the model. Reads never block. If a write action was previously performed,
* the value read will be from an uncommitted copy of the the management model.
* <p>
* Note: By default the returned resource is read-only copy of the entire sub-model. In case the entire sub-model
* is not required use {@link OperationContext#readResourceFromRoot(PathAddress, boolean)} instead.
*
* @param address the (possibly empty) address
* @return a read-only reference from the model
*/
Resource readResourceFromRoot(PathAddress address);
/**
* Read an addressable resource from the root of the model. Reads never block. If a write action was previously performed,
* the value read will be from an uncommitted copy of the the management model.
* <p>
* Use the {@code recursive} parameter to avoid the expense of making read-only copies of large portions of the
* resource tree. If {@code recursive} is {@code false}, the returned resource will only have placeholder resources
* for immediate children. Those placeholder resources will return an empty
* {@link org.jboss.as.controller.registry.Resource#getModel() model} and will not themselves have any children.
* Their presence, however, allows the caller to see what immediate children exist under the target resource.
*
* @param address the (possibly empty) address
* @param recursive whether the model should be read recursively or not
* @return a read-only reference from the model
*/
Resource readResourceFromRoot(PathAddress address, boolean recursive);
/**
* Get an addressable resource for update operations. Since only one operation may write at a time, this operation
* may block until other writing operations have completed.
*
* @param relativeAddress the (possibly empty) address where the resource should be added. The address is relative to the
* address of the operation being executed
* @return the resource
*/
Resource readResourceForUpdate(PathAddress relativeAddress);
/**
* Remove a resource relative to the executed operation address. Since only one operation
* may write at a time, this operation may block until other writing operations have completed.
*
* @param relativeAddress the (possibly empty) address where the resource should be removed. The address is relative to the
* address of the operation being executed
* @return the old value of the node
* @throws UnsupportedOperationException if the calling operation is not a model operation
*/
Resource removeResource(PathAddress relativeAddress) throws UnsupportedOperationException;
/**
* Get a read-only reference of the entire management model BEFORE any changes were made by this context.
* The structure of the returned model may depend on the context type (domain vs. server).
*
* @return the read-only original resource
*/
Resource getOriginalRootResource();
/**
* Determine whether the model has thus far been affected by this operation.
*
* @return {@code true} if the model was affected, {@code false} otherwise
*/
boolean isModelAffected();
/**
* Determine whether the {@link ManagementResourceRegistration management resource registry} has thus far been affected by this operation.
*
* @return {@code true} if the management resource registry was affected, {@code false} otherwise
*/
boolean isResourceRegistryAffected();
/**
* Determine whether the runtime container has thus far been affected by this operation.
*
* @return {@code true} if the container was affected, {@code false} otherwise
*/
boolean isRuntimeAffected();
/**
* Get the current stage of execution.
*
* @return the current stage
*/
Stage getCurrentStage();
/**
* Send a message to the client. Valid only during this operation.
*
* @param severity the message severity
* @param message the message
*/
void report(MessageSeverity severity, String message);
/**
* Marks a resource to indicate that it's backing service(s) will be restarted.
* This is to ensure that a restart only occurs once, even if there are multiple updates.
* When true is returned the caller has "acquired" the mark and should proceed with the
* restart, when false, the caller should take no additional action.
*
* The passed owner is compared by instance when a call to {@link #revertReloadRequired()}.
* This is to ensure that only the "owner" will be successful in reverting the mark.
*
* @param resource the resource that will be restarted
* @param owner the instance representing ownership of the mark
* @return true if the mark was required and the service should be restarted,
* false if no action should be taken.
*/
boolean markResourceRestarted(PathAddress resource, Object owner);
/**
* Removes the restarted marking on the specified resource, provided the passed owner is the one
* originally used to obtain the mark. The purpose of this method is to facilitate rollback processing.
* Only the "owner" of the mark should be the one to revert the service to a previous state (once again
* restarting it).When true is returned, the caller must take the required corrective
* action by restarting the resource, when false is returned the caller should take no additional action.
*
* The passed owner is compared by instance to the one provided in {@link #markResourceRestarted(PathAddress, Object)}
*
* @param resource the resource being reverted
* @param owner the owner of the mark for the resource
* @return true if the caller owns the mark and the service should be restored by restarting
* false if no action should be taken.
*/
boolean revertResourceRestarted(PathAddress resource, Object owner);
/**
* Resolves any expressions in the passed in ModelNode.
* Expressions may either represent system properties or vaulted date. For vaulted data the format is
* ${VAULT::vault_block::attribute_name::sharedKey}
*
* @param node the ModelNode containing expressions.
* @return a copy of the node with expressions resolved
*
* @throws OperationFailedException if there is a value of type {@link org.jboss.dmr.ModelType#EXPRESSION} in the node tree and
* there is no system property or environment variable that matches the expression, or if a security
* manager exists and its {@link SecurityManager#checkPermission checkPermission} method doesn't allow
* access to the relevant system property or environment variable
*/
ModelNode resolveExpressions(ModelNode node) throws OperationFailedException;
/**
* Retrieves an object that has been attached to this context.
*
* @param key the key to the attachment.
* @param <T> the value type of the attachment.
*
* @return the attachment if found otherwise {@code null}.
*/
<T> T getAttachment(AttachmentKey<T> key);
/**
* Attaches an arbitrary object to this context.
*
* @param key they attachment key used to ensure uniqueness and used for retrieval of the value.
* @param value the value to store.
* @param <T> the value type of the attachment.
*
* @return the previous value associated with the key or {@code null} if there was no previous value.
*/
<T> T attach(AttachmentKey<T> key, T value);
/**
* Attaches an arbitrary object to this context only if the object was not already attached. If a value has already
* been attached with the key provided, the current value associated with the key is returned.
*
* @param key they attachment key used to ensure uniqueness and used for retrieval of the value.
* @param value the value to store.
* @param <T> the value type of the attachment.
*
* @return the previous value associated with the key or {@code null} if there was no previous value.
*/
<T> T attachIfAbsent(AttachmentKey<T> key, T value);
/**
* Detaches or removes the value from this context.
*
* @param key the key to the attachment.
* @param <T> the value type of the attachment.
*
* @return the attachment if found otherwise {@code null}.
*/
<T> T detach(AttachmentKey<T> key);
/**
* Check for authorization of the given operation.
* @param operation the operation. Cannot be {@code null}
* @return the authorization result
*/
AuthorizationResult authorize(ModelNode operation);
/**
* Check for authorization of the given effects for the given operation.
* @param operation the operation. Cannot be {@code null}
* @param effects the relevant effects. If empty, all effects associated with the operation are tested.
* @return the authorization result
*/
AuthorizationResult authorize(ModelNode operation, Set<Action.ActionEffect> effects);
/**
* Check for authorization for the resource associated with the currently executing operation step and,
* optionally, its individual attributes
* @param attributes {@code true} if the result should include attribute authorizations
* @param isDefaultResource {@code true} if
* @return the resource authorization
*/
ResourceAuthorization authorizeResource(boolean attributes, boolean isDefaultResource);
/**
* Check for authorization to read or modify an attribute, checking all effects of the given operation
* @param operation the operation that will read or modify
* @param attribute the attribute name
* @param currentValue the current value of the attribute
* @return the authorization result
*/
AuthorizationResult authorize(ModelNode operation, String attribute, ModelNode currentValue);
/**
* Check for authorization to read or modify an attribute, limiting the check to the given effects of the operation
* @param operation the operation that will read or modify
* @param attribute the attribute name
* @param currentValue the current value of the attribute
* @param effects the effects to check, or, if empty, all effects
* @return the authorization result
*/
AuthorizationResult authorize(ModelNode operation, String attribute, ModelNode currentValue, Set<Action.ActionEffect> effects);
/**
* Check for authorization to execute an operation.
*
* @param operation the operation. Cannot be {@code null}
* @return the authorization result
*/
AuthorizationResult authorizeOperation(ModelNode operation);
/**
* Obtain the {@link Caller} for the current request.
*
* @return The current caller.
* @deprecated Use {@link #getSecurityIdentity()} instead.
*/
@Deprecated
Caller getCaller();
/**
* Obtain the {@link SecurityIdentity} for the current request.
*
* @return The current {@code SecurityIdentity}.
*/
SecurityIdentity getSecurityIdentity();
/**
* Emit a {@link org.jboss.as.controller.notification.Notification}.
*
* @param notification the notification to emit
*/
void emit(final Notification notification);
/**
* Gets the caller environment for the current request.
* @return the call environment
*/
Environment getCallEnvironment();
/**
* deprecated use {@link #registerCapability(RuntimeCapability)}
* Registers a capability with the system. Any {@link org.jboss.as.controller.capability.RuntimeCapability#getRequirements() requirements}
* associated with the capability will be recorded as requirements.
*
* @param capability the capability. Cannot be {@code null}
* @param attribute the name of the attribute that triggered this registration, or {@code null} if no single
* attribute was responsible
*
* @throws java.lang.IllegalStateException if {@link #getCurrentStage() the current stage} is not {@link Stage#MODEL}
* @deprecated use {@link #registerCapability(RuntimeCapability)}
*/
@Deprecated
void registerCapability(RuntimeCapability capability, String attribute);
/**
* Registers a capability with the system. Any {@link org.jboss.as.controller.capability.RuntimeCapability#getRequirements() requirements}
* associated with the capability will be recorded as requirements.
*
* @param capability the capability. Cannot be {@code null}
*
* @throws java.lang.IllegalStateException if {@link #getCurrentStage() the current stage} is not {@link Stage#MODEL}
*/
void registerCapability(RuntimeCapability capability);
/**
* Registers an additional hard requirement a capability has beyond what it was aware of when {@code capability}
* was passed to {@link #registerCapability(org.jboss.as.controller.capability.RuntimeCapability, String)}. Used for cases
* where a capability optionally depends on another capability, and whether or not that requirement is needed is
* not known when the capability is first registered.
* <p>
* This method should be used in preference to {@link #requireOptionalCapability(String, String, String)}
* when, based on its own configuration, the caller knows in {@link org.jboss.as.controller.OperationContext.Stage#MODEL}
* that the optional capability is actually required in the current process.
* </p>
*
* @param required the name of the required capability. Cannot be {@code null}
* @param dependent the name of the capability that requires the other capability. Cannot be {@code null}
* @param attribute the name of the attribute that triggered this requirement, or {@code null} if no single
* attribute was responsible
*
* @throws java.lang.IllegalStateException if {@link #getCurrentStage() the current stage} is not {@link Stage#MODEL}
* or if {@code capability} is not registered
*/
void registerAdditionalCapabilityRequirement(String required, String dependent, String attribute);
/**
* Checks whether one of a capability's optional and runtime-only requirements is present. Only for use in cases
* where the {@code dependent} capability's persistent configuration does not <strong>mandate</strong> the presence
* of the {@code requested} capability, but the capability will use it at runtime if it is present.
* <p>
* This method should be used in preference to {@link #registerAdditionalCapabilityRequirement(String, String, String)}
* when the caller's own configuration doesn't impose a hard requirement for the {@code requested} capability, but,
* if it is present it will be used. Once the caller declares an intent to use the capability by invoking this
* method and getting a {@code true} response, thereafter the system is aware that {@code dependent} is actually
* using {@code requested}, but <strong>will not</strong> prevent configuration changes that make {@code requested}
* unavailable.
* </p>
*
* @param requested the name of the requested capability. Cannot be {@code null}
* @param dependent the name of the capability that requires the other capability. Cannot be {@code null}
* @param attribute the name of the attribute that triggered this requirement, or {@code null} if no single
* attribute was responsible
* @return {@code true} if the requested capability is present; {@code false} if not. If {@code true}, hereafter
* {@code dependent}'s requirement for {@code requested} will not be treated as optional.
*
* @throws java.lang.IllegalStateException if {@link #getCurrentStage() the current stage} is {@link Stage#MODEL}. The
* complete set of capabilities is not known until the end of the model stage.
*/
boolean hasOptionalCapability(String requested, String dependent, String attribute);
/**
* Requests that one of a capability's optional requirements hereafter be treated as required, until the process is
* stopped or reloaded. This request will only be granted if the required capability is already present; otherwise
* an {@link org.jboss.as.controller.OperationFailedException} will be thrown.
* <p>
* This method should be used in preference to {@link #registerAdditionalCapabilityRequirement(String, String, String)}
* only if the caller is not sure whether the capability is required until {@link org.jboss.as.controller.OperationContext.Stage#RUNTIME}.
* <strong>Not knowing whether a capability is required until stage RUNTIME is an anti-pattern, so use of this
* method is strongly discouraged.</strong> It only exists to avoid the need to break backward compatibility by removing
* support for expressions from certain attributes.
* </p>
*
* @param required the name of the required capability. Cannot be {@code null}
* @param dependent the name of the capability that requires the other capability. Cannot be {@code null}
* @param attribute the name of the attribute that triggered this requirement, or {@code null} if no single
* attribute was responsible
*
* @throws java.lang.IllegalStateException if {@link #getCurrentStage() the current stage} is {@link Stage#MODEL}. The
* complete set of capabilities is not known until the end of the model stage.
* @throws org.jboss.as.controller.OperationFailedException if the requested capability is not available
*/
void requireOptionalCapability(String required, String dependent, String attribute) throws OperationFailedException;
/**
* Record that a previously registered requirement for a capability will no longer exist.
* <p>
* <strong>Semantics with "reload-required" and "restart-required":</strong>
* Deregistering a capability requirement does not obligate the caller to cease using a
* {@link #getCapabilityRuntimeAPI(String, Class) previously obtained} reference to that
* capability's {@link org.jboss.as.controller.capability.RuntimeCapability#getRuntimeAPI() runtime API}. But, if
* the caller will not cease using the capability, it must put the process in {@link #reloadRequired() reload-required}
* or {@link #restartRequired() restart-required} state. This will reflect the fact that the model says the
* capability is not required, but in the runtime it still is.
* </p>
*
* @param required the name of the no longer required capability
* @param dependent the name of the capability that no longer has the requirement
*
* @throws java.lang.IllegalStateException if {@link #getCurrentStage() the current stage} is not {@link Stage#MODEL}
*/
void deregisterCapabilityRequirement(String required, String dependent);
/**
* Record that a previously registered capability will no longer be available. Invoking this operation will also
* automatically {@link #deregisterCapabilityRequirement(String, String) deregister any requirements} that are
* associated with this capability, including optional ones.
* <p><strong>Semantics with "reload-required" and "restart-required":</strong>
* Deregistering a capability does not eliminate the obligation to other capabilities that have
* previously depended upon it to support them by providing expected runtime services. It does require that those other
* capabilities also {@link #deregisterCapabilityRequirement(String, String) deregister their requirements} as
* part of the same operation. Requiring that they do so ensures that the management model is consistent.
* However, those other capabilities may simply put the process in {@code reload-required}
* or {@code restart-required} state and then continue to use the existing services. So, an operation that invokes
* this method should also always put the process into {@code reload-required} or {@code restart-required} state.
* This will reflect the fact that the model says the capability is not present, but in the runtime it still is.
* </p>
*
* @param capabilityName the name of the capability
*
* @throws java.lang.IllegalStateException if {@link #getCurrentStage() the current stage} is not {@link Stage#MODEL}
*/
void deregisterCapability(String capabilityName);
/**
* Gets the runtime API associated with a given capability, if there is one.
* @param capabilityName the name of the capability. Cannot be {@code null}
* @param apiType class of the java type that exposes the API. Cannot be {@code null}
* @param <T> the java type that exposes the API
* @return the runtime API. Will not return {@code null}
*
* @throws java.lang.IllegalStateException if {@link #getCurrentStage() the current stage} is {@link Stage#MODEL}. The
* complete set of capabilities is not known until the end of the model stage.
* @throws java.lang.IllegalArgumentException if the capability does not provide a runtime API
* @throws java.lang.ClassCastException if the runtime API exposed by the capability cannot be cast to type {code T}
*/
<T> T getCapabilityRuntimeAPI(String capabilityName, Class<T> apiType);
/**
* Gets the runtime API associated with a given {@link RuntimeCapability#isDynamicallyNamed() dynamically named}
* capability, if there is one.
*
* @param capabilityBaseName the base name of the capability. Cannot be {@code null}
* @param dynamicPart the dynamic part of the capability name. Cannot be {@code null}
* @param apiType class of the java type that exposes the API. Cannot be {@code null}
* @param <T> the java type that exposes the API
* @return the runtime API. Will not return {@code null}
*
* @throws java.lang.IllegalStateException if {@link #getCurrentStage() the current stage} is {@link Stage#MODEL}. The
* complete set of capabilities is not known until the end of the model stage.
* @throws java.lang.IllegalArgumentException if the capability does not provide a runtime API
* @throws java.lang.ClassCastException if the runtime API exposed by the capability cannot be cast to type {code T}
*/
<T> T getCapabilityRuntimeAPI(String capabilityBaseName, String dynamicPart, Class<T> apiType);
/**
* Gets the name of a service associated with a given capability, if there is one.
* @param capabilityName the name of the capability. Cannot be {@code null}
* @param serviceType class of the java type that exposes by the service. Cannot be {@code null}
* @return the name of the service. Will not return {@code null}
*
* @throws java.lang.IllegalStateException if {@link #getCurrentStage() the current stage} is {@link Stage#MODEL}. The
* complete set of capabilities is not known until the end of the model stage.
* @throws IllegalArgumentException if {@code serviceType} is {@code null } or
* the capability does not provide a service of type {@code serviceType}
*/
ServiceName getCapabilityServiceName(String capabilityName, Class<?> serviceType);
/**
* Gets the name of a service associated with a given {@link RuntimeCapability#isDynamicallyNamed() dynamically named}
* capability, if there is one.
*
* @param capabilityBaseName the base name of the capability. Cannot be {@code null}
* @param dynamicPart the dynamic part of the capability name. Cannot be {@code null}
* @param serviceType class of the java type that exposes by the service. Cannot be {@code null}
* @return the name of the service. Will not return {@code null}
*
* @throws java.lang.IllegalStateException if {@link #getCurrentStage() the current stage} is {@link Stage#MODEL}. The
* complete set of capabilities is not known until the end of the model stage.
* @throws IllegalArgumentException if {@code serviceType} is {@code null } or
* the capability does not provide a service of type {@code serviceType}
*/
ServiceName getCapabilityServiceName(String capabilityBaseName, String dynamicPart, Class<?> serviceType);
/**
* Gets a support object that allows service implementations installed from this context to
* integrate with capabilities.
*
* @return the support object. Will not return {@code null}
*
* * @throws java.lang.IllegalStateException if {@link #getCurrentStage() the current stage} is {@link Stage#MODEL}.
* Service integration is not supported in the model stage.
*/
CapabilityServiceSupport getCapabilityServiceSupport();
/**
* Whether normally this operation would require a runtime step. It returns {@code true in the following cases}
* <ul>
* <li>The process is a server, and it is running in NORMAL (i.e. not admin-only) mode.</li>
* <li>The process is a HC, and the address of the operation is a subsystem in the host model or a child thereof</li>
*/
boolean isDefaultRequiresRuntime();
/**
* The stage at which a step should apply.
*/
enum Stage {
/**
* The step applies to the model (read or write).
*/
MODEL,
/**
* The step applies to the runtime container (read or write).
*/
RUNTIME,
/**
* The step checks the result of a runtime container operation (read only). Inspect the container,
* and if problems are detected, record the problem(s) in the operation result.
*/
VERIFY,
/**
* The step performs any actions needed to cause the operation to take effect on the relevant servers
* in the domain. Adding a step in this stage is only allowed when the process type is {@link ProcessType#HOST_CONTROLLER}.
*/
DOMAIN,
/**
* The operation has completed execution.
*/
DONE;
Stage() {
}
boolean hasNext() {
return this != DONE;
}
Stage next() {
switch (this) {
case MODEL: return RUNTIME;
case RUNTIME: return VERIFY;
case VERIFY: return DOMAIN;
case DOMAIN: return DONE;
case DONE:
default: throw new IllegalStateException();
}
}
}
/**
* The result action.
*/
enum ResultAction {
/**
* The operation will be committed to the model and/or runtime.
*/
KEEP,
/**
* The operation will be reverted.
*/
ROLLBACK,
}
/**
* Handler for a callback to an {@link OperationStepHandler} indicating that the overall operation is being
* rolled back and the handler should revert any change it has made.
*/
@FunctionalInterface
interface RollbackHandler {
/**
* A {@link RollbackHandler} that does nothing in the callback. Intended for use by operation step
* handlers that do not need to do any clean up work -- e.g. those that only perform reads or those
* that only perform persistent configuration changes. (Persistent configuration changes need not be
* explicitly rolled back as the {@link OperationContext} will handle that automatically.)
*/
RollbackHandler NOOP_ROLLBACK_HANDLER = new RollbackHandler() {
/**
* Does nothing.
*
* @param context ignored
* @param operation ignored
*/
@Override
public void handleRollback(OperationContext context, ModelNode operation) {
// no-op
}
};
/**
* A {@link RollbackHandler} that calls {@link OperationContext#revertReloadRequired()}. Intended for use by
* operation step handlers call {@link OperationContext#reloadRequired()} and perform no other actions
* that need to be rolled back.
*/
RollbackHandler REVERT_RELOAD_REQUIRED_ROLLBACK_HANDLER = new RollbackHandler() {
/**
* Does nothing.
*
* @param context ignored
* @param operation ignored
*/
@Override
public void handleRollback(OperationContext context, ModelNode operation) {
context.revertReloadRequired();
}
};
/**
* Callback to an {@link OperationStepHandler} indicating that the overall operation is being rolled back and the
* handler should revert any change it has made. A handler executing in {@link Stage#MODEL} need not revert any changes
* it has made to the configuration model; this will be done automatically. A handler need not to remove services
* created by the operation; this will be done automatically.
*
* @param context the operation execution context; will be the same as what was passed to the
* {@link OperationStepHandler#execute(OperationContext, ModelNode)} method invocation
* that registered this rollback handler.
* @param operation the operation being rolled back; will be the same as what was passed to the
* {@link OperationStepHandler#execute(OperationContext, ModelNode)} method invocation
* that registered this rollback handler.
*/
void handleRollback(OperationContext context, ModelNode operation);
}
/**
* Handler for a callback to an {@link OperationStepHandler} indicating that the result of the overall operation is
* known and the handler can take any necessary actions to deal with that result.
*/
@FunctionalInterface
interface ResultHandler {
/**
* A {@link ResultHandler} that does nothing in the callback. Intended for use by operation step
* handlers that do not need to do any clean up work -- e.g. those that only perform reads or those
* that only perform persistent configuration changes. (Persistent configuration changes need not be
* explicitly rolled back as the {@link OperationContext} will handle that automatically.)
*/
ResultHandler NOOP_RESULT_HANDLER = new ResultHandler() {
/**
* Does nothing.
*
* @param resultAction ignored
* @param context ignored
* @param operation ignored
*/
@Override
public void handleResult(ResultAction resultAction, OperationContext context, ModelNode operation) {
// no-op
}
};
/**
* Callback to an {@link OperationStepHandler} indicating that the result of the overall operation is
* known and the handler can take any necessary actions to deal with that result.
*
* @param resultAction the overall result of the operation
* @param context the operation execution context; will be the same as what was passed to the
* {@link OperationStepHandler#execute(OperationContext, ModelNode)} method invocation
* that registered this rollback handler.
* @param operation the operation being rolled back; will be the same as what was passed to the
* {@link OperationStepHandler#execute(OperationContext, ModelNode)} method invocation
* that registered this rollback handler.
*/
void handleResult(ResultAction resultAction, OperationContext context, ModelNode operation);
}
/**
* An attachment key instance.
*
* @param <T> the attachment value type
*/
@SuppressWarnings("UnusedDeclaration")
public static final class AttachmentKey<T> {
private final Class<T> valueClass;
/**
* Construct a new instance.
*
* @param valueClass the value type.
*/
private AttachmentKey(final Class<T> valueClass) {
this.valueClass = valueClass;
}
/**
* Cast the value to the type of this attachment key.
*
* @param value the value
*
* @return the cast value
*/
public T cast(final Object value) {
return valueClass.cast(value);
}
/**
* Construct a new simple attachment key.
*
* @param valueClass the value class
* @param <T> the attachment type
*
* @return the new instance
*/
@SuppressWarnings("unchecked")
public static <T> AttachmentKey<T> create(final Class<? super T> valueClass) {
return new AttachmentKey(valueClass);
}
}
/** The current activity of an operation. */
public static enum ExecutionStatus {
EXECUTING("executing"),
AWAITING_OTHER_OPERATION("awaiting-other-operation"),
AWAITING_STABILITY("awaiting-stability"),
COMPLETING("completing"),
ROLLING_BACK("rolling-back");
private final String name;
private ExecutionStatus(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
}