/* * JBoss, Home of Professional Open Source. * Copyright 2013, 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 java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Supplier; import org.jboss.as.controller.access.Action; import org.jboss.as.controller.access.AuthorizationResult; 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.logging.ControllerLogger; import org.jboss.as.controller.notification.Notification; import org.jboss.as.controller.persistence.ConfigurationPersistenceException; import org.jboss.as.controller.persistence.ConfigurationPersister; 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.common.Assert; import org.wildfly.security.auth.server.SecurityIdentity; /** * A read-only {@linkplain OperationContext}, allowing read-only access to the current write model from a different * operation, preventing any writes from this context. Operations can acquire a controller lock to prevent other * writes happen until this operation is done. * * @author Emanuel Muckenhuber */ class ReadOnlyContext extends AbstractOperationContext { private final int operationId; private final ModelControllerImpl controller; private final AbstractOperationContext primaryContext; private final ModelControllerImpl.ManagementModelImpl managementModel; private Step lockStep; private final ConcurrentMap<AttachmentKey<?>, Object> valueAttachments = new ConcurrentHashMap<AttachmentKey<?>, Object>(); ReadOnlyContext(final ProcessType processType, final RunningMode runningMode, final ModelController.OperationTransactionControl transactionControl, final ControlledProcessState processState, final boolean booting, final ModelControllerImpl.ManagementModelImpl managementModel, final AbstractOperationContext primaryContext, final ModelControllerImpl controller, final int operationId, final Supplier<SecurityIdentity> securityIdentitySupplier) { super(processType, runningMode, transactionControl, processState, booting, controller.getAuditLogger(), controller.getNotificationSupport(), controller, true, null, securityIdentitySupplier); this.primaryContext = primaryContext; this.controller = controller; this.operationId = operationId; this.managementModel = managementModel; } @Override ResultAction executeOperation() { // WFCORE-2 allow this thread to be treated as a controlling thread AbstractOperationContext.controllingThread.set(primaryContext.initiatingThread); try { return super.executeOperation(); } finally { AbstractOperationContext.controllingThread.remove(); } } @Override ModelControllerImpl.ManagementModelImpl getManagementModel() { return managementModel; } @Override void awaitServiceContainerStability() throws InterruptedException { // nothing here } @Override ConfigurationPersister.PersistenceResource createPersistenceResource() throws ConfigurationPersistenceException { throw readOnlyContext(); } @Override void publishCapabilityRegistry() { //do noting } @Override void operationRollingBack() { // don't need to do anything } @Override void waitForRemovals() throws InterruptedException { // nothing here } @Override boolean isReadOnly() { return true; } @Override ManagementResourceRegistration getRootResourceRegistrationForUpdate() { throw readOnlyContext(); } @Override public InputStream getAttachmentStream(int index) { throw readOnlyContext(); } @Override public int getAttachmentStreamCount() { return 0; } @Override public boolean isRollbackOnRuntimeFailure() { return false; } @Override public boolean isResourceServiceRestartAllowed() { return false; } @Override public ImmutableManagementResourceRegistration getResourceRegistration() { return managementModel.getRootResourceRegistration().getSubModel(activeStep.address); } @Override public ManagementResourceRegistration getResourceRegistrationForUpdate() { throw readOnlyContext(); } @Override public ImmutableManagementResourceRegistration getRootResourceRegistration() { return managementModel.getRootResourceRegistration(); } @Override public ServiceRegistry getServiceRegistry(boolean modify) throws UnsupportedOperationException { if (modify) { throw readOnlyContext(); } return primaryContext.getServiceRegistry(false); } @Override public ServiceController<?> removeService(ServiceName name) throws UnsupportedOperationException { throw readOnlyContext(); } @Override public void removeService(ServiceController<?> controller) throws UnsupportedOperationException { throw readOnlyContext(); } @Override public ServiceTarget getServiceTarget() throws UnsupportedOperationException { return primaryContext.getServiceTarget(); } @Override public void acquireControllerLock() { if (lockStep == null) { try { controller.acquireWriteLock(operationId, true); lockStep = activeStep; } catch (InterruptedException e) { cancelled = true; Thread.currentThread().interrupt(); throw ControllerLogger.ROOT_LOGGER.operationCancelledAsynchronously(); } } } @Override void releaseStepLocks(Step step) { if (step == lockStep) { lockStep = null; controller.releaseWriteLock(operationId); } } @Override public Resource createResource(PathAddress address) throws UnsupportedOperationException { throw readOnlyContext(); } @Override public void addResource(PathAddress address, Resource toAdd) { throw readOnlyContext(); } @Override public void addResource(PathAddress address, int index, Resource toAdd) { throw readOnlyContext(); } public Resource readResource(final PathAddress requestAddress) { return readResource(requestAddress, true); } public Resource readResource(final PathAddress requestAddress, final boolean recursive) { final PathAddress address = activeStep.address.append(requestAddress); return readResourceFromRoot(address, recursive); } public Resource readResourceFromRoot(final PathAddress address) { return readResourceFromRoot(address, true); } public Resource readResourceFromRoot(final PathAddress address, final boolean recursive) { return readResourceFromRoot(managementModel, address, recursive); } @Override Resource readResourceFromRoot(ManagementModel model, PathAddress address, boolean recursive) { return primaryContext.readResourceFromRoot(model, address, recursive); } @Override public Resource readResourceForUpdate(PathAddress relativeAddress) { throw readOnlyContext(); } @Override public Resource removeResource(PathAddress relativeAddress) throws UnsupportedOperationException { throw readOnlyContext(); } @Override public Resource getOriginalRootResource() { return primaryContext.getOriginalRootResource(); } @Override public boolean isModelAffected() { return false; } @Override public boolean isResourceRegistryAffected() { return primaryContext.isResourceRegistryAffected(); } @Override public boolean isRuntimeAffected() { return primaryContext.isRuntimeAffected(); } @Override public Stage getCurrentStage() { return currentStage; } @Override public void report(MessageSeverity severity, String message) { // primaryContext.report(severity, message); } @Override public boolean markResourceRestarted(PathAddress resource, Object owner) { return false; } @Override public boolean revertResourceRestarted(PathAddress resource, Object owner) { return false; } @Override public ModelNode resolveExpressions(ModelNode node) throws OperationFailedException { return primaryContext.resolveExpressions(node); } @Override public <T> T getAttachment(AttachmentKey<T> key) { if (valueAttachments.containsKey(key)) { return key.cast(valueAttachments.get(key)); } return primaryContext.getAttachment(key); } @Override public <V> V attach(final AttachmentKey<V> key, final V value) { Assert.checkNotNullParam("key", key); return key.cast(valueAttachments.put(key, value)); } @Override public <V> V attachIfAbsent(final AttachmentKey<V> key, final V value) { Assert.checkNotNullParam("key", key); return key.cast(valueAttachments.putIfAbsent(key, value)); } @Override public <V> V detach(final AttachmentKey<V> key) { Assert.checkNotNullParam("key", key); return key.cast(valueAttachments.remove(key)); } @Override public AuthorizationResult authorize(ModelNode operation) { return primaryContext.authorize(operation); } @Override public AuthorizationResult authorize(ModelNode operation, Set<Action.ActionEffect> effects) { return primaryContext.authorize(operation, effects); } @Override public AuthorizationResult authorize(ModelNode operation, String attribute, ModelNode currentValue) { return primaryContext.authorize(operation, attribute, currentValue); } @Override public AuthorizationResult authorize(ModelNode operation, String attribute, ModelNode currentValue, Set<Action.ActionEffect> effects) { return primaryContext.authorize(operation, attribute, currentValue, effects); } IllegalStateException readOnlyContext() { return ControllerLogger.ROOT_LOGGER.readOnlyContext(); } @Override public AuthorizationResult authorizeOperation(ModelNode operation) { return primaryContext.authorizeOperation(operation); } @Override public ResourceAuthorization authorizeResource(boolean attributes, boolean isDefaultResource) { return primaryContext.authorizeResource(attributes, isDefaultResource); } @Override public void emit(Notification notification) { throw readOnlyContext(); } Resource getModel() { return primaryContext.getModel(); } @Override @Deprecated public void registerCapability(RuntimeCapability capability, String attribute) { throw readOnlyContext(); } @Override public void registerCapability(RuntimeCapability capability) { throw readOnlyContext(); } @Override public void registerAdditionalCapabilityRequirement(String required, String dependent, String attribute) { throw readOnlyContext(); } @Override public boolean hasOptionalCapability(String required, String dependent, String attribute) { throw readOnlyContext(); } @Override public void requireOptionalCapability(String required, String dependent, String attribute) { throw readOnlyContext(); } @Override public void deregisterCapabilityRequirement(String required, String dependent) { throw readOnlyContext(); } @Override public void deregisterCapability(String capability) { throw readOnlyContext(); } @Override public <T> T getCapabilityRuntimeAPI(String capabilityName, Class<T> apiType) { throw readOnlyContext(); } @Override public <T> T getCapabilityRuntimeAPI(String capabilityBaseName, String dynamicPart, Class<T> apiType) { throw readOnlyContext(); } @Override public ServiceName getCapabilityServiceName(String capabilityName, Class<?> type) { throw readOnlyContext(); } @Override public ServiceName getCapabilityServiceName(String capabilityBaseName, String dynamicPart, Class<?> serviceType) { throw readOnlyContext(); } @Override public CapabilityServiceSupport getCapabilityServiceSupport() { throw readOnlyContext(); } }