/*
* 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.wildfly.extension.messaging.activemq;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PATH;
import static org.wildfly.extension.messaging.activemq.Capabilities.ACTIVEMQ_SERVER_CAPABILITY;
import static org.wildfly.extension.messaging.activemq.Capabilities.ELYTRON_DOMAIN_CAPABILITY;
import static org.wildfly.extension.messaging.activemq.Capabilities.JMX_CAPABILITY;
import static org.wildfly.extension.messaging.activemq.CommonAttributes.ADDRESS_SETTING;
import static org.wildfly.extension.messaging.activemq.CommonAttributes.BINDINGS_DIRECTORY;
import static org.wildfly.extension.messaging.activemq.CommonAttributes.BROADCAST_GROUP;
import static org.wildfly.extension.messaging.activemq.CommonAttributes.DISCOVERY_GROUP;
import static org.wildfly.extension.messaging.activemq.CommonAttributes.HTTP_ACCEPTOR;
import static org.wildfly.extension.messaging.activemq.CommonAttributes.INCOMING_INTERCEPTORS;
import static org.wildfly.extension.messaging.activemq.CommonAttributes.JGROUPS_CHANNEL;
import static org.wildfly.extension.messaging.activemq.CommonAttributes.JOURNAL_DIRECTORY;
import static org.wildfly.extension.messaging.activemq.CommonAttributes.LARGE_MESSAGES_DIRECTORY;
import static org.wildfly.extension.messaging.activemq.CommonAttributes.MODULE;
import static org.wildfly.extension.messaging.activemq.CommonAttributes.NAME;
import static org.wildfly.extension.messaging.activemq.CommonAttributes.OUTGOING_INTERCEPTORS;
import static org.wildfly.extension.messaging.activemq.CommonAttributes.PAGING_DIRECTORY;
import static org.wildfly.extension.messaging.activemq.CommonAttributes.SECURITY_SETTING;
import static org.wildfly.extension.messaging.activemq.PathDefinition.PATHS;
import static org.wildfly.extension.messaging.activemq.PathDefinition.RELATIVE_TO;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.ASYNC_CONNECTION_EXECUTION_ENABLED;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.CLUSTER_PASSWORD;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.CLUSTER_USER;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.CONNECTION_TTL_OVERRIDE;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.CREATE_BINDINGS_DIR;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.CREATE_JOURNAL_DIR;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.ELYTRON_DOMAIN;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.ID_CACHE_SIZE;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JMX_DOMAIN;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JMX_MANAGEMENT_ENABLED;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_BINDINGS_TABLE;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_BUFFER_SIZE;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_BUFFER_TIMEOUT;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_COMPACT_MIN_FILES;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_COMPACT_PERCENTAGE;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_DATABASE;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_DATASOURCE;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_FILE_SIZE;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_JDBC_NETWORK_TIMEOUT;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_JMS_BINDINGS_TABLE;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_LARGE_MESSAGES_TABLE;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_MAX_IO;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_MESSAGES_TABLE;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_MIN_FILES;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_PAGE_STORE_TABLE;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_POOL_FILES;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_SYNC_NON_TRANSACTIONAL;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_SYNC_TRANSACTIONAL;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.JOURNAL_TYPE;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.LOG_JOURNAL_WRITE_RATE;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.MANAGEMENT_ADDRESS;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.MANAGEMENT_NOTIFICATION_ADDRESS;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.MEMORY_MEASURE_INTERVAL;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.MEMORY_WARNING_THRESHOLD;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.MESSAGE_COUNTER_MAX_DAY_HISTORY;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.MESSAGE_COUNTER_SAMPLE_PERIOD;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.MESSAGE_EXPIRY_SCAN_PERIOD;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.MESSAGE_EXPIRY_THREAD_PRIORITY;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.OVERRIDE_IN_VM_SECURITY;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.PAGE_MAX_CONCURRENT_IO;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.PERF_BLAST_PAGES;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.PERSISTENCE_ENABLED;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.PERSIST_DELIVERY_COUNT_BEFORE_DELIVERY;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.PERSIST_ID_CACHE;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.RUN_SYNC_SPEED_TEST;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.SCHEDULED_THREAD_POOL_MAX_SIZE;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.SECURITY_DOMAIN;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.SECURITY_ENABLED;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.SECURITY_INVALIDATION_INTERVAL;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.SERVER_DUMP_INTERVAL;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.STATISTICS_ENABLED;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.THREAD_POOL_MAX_SIZE;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.TRANSACTION_TIMEOUT;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.TRANSACTION_TIMEOUT_SCAN_PERIOD;
import static org.wildfly.extension.messaging.activemq.ServerDefinition.WILD_CARD_ROUTING_ENABLED;
import static org.wildfly.extension.messaging.activemq.ha.HAPolicyConfigurationBuilder.addHAPolicyConfiguration;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.management.MBeanServer;
import javax.sql.DataSource;
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
import org.apache.activemq.artemis.api.core.BroadcastGroupConfiguration;
import org.apache.activemq.artemis.api.core.DiscoveryGroupConfiguration;
import org.apache.activemq.artemis.api.core.Interceptor;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.config.BridgeConfiguration;
import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl;
import org.apache.activemq.artemis.core.config.storage.DatabaseStorageConfiguration;
import org.apache.activemq.artemis.core.security.Role;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.JournalType;
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
import org.jboss.as.controller.AbstractAddStepHandler;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.ObjectTypeAttributeDefinition;
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.as.controller.PathElement;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.registry.Resource;
import org.jboss.as.controller.security.CredentialReference;
import org.jboss.as.controller.services.path.PathManager;
import org.jboss.as.controller.services.path.PathManagerService;
import org.jboss.as.network.OutboundSocketBinding;
import org.jboss.as.network.SocketBinding;
import org.jboss.as.security.plugins.SecurityDomainContext;
import org.jboss.as.security.service.SecurityDomainService;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.Property;
import org.jboss.modules.Module;
import org.jboss.modules.ModuleIdentifier;
import org.jboss.msc.service.ServiceBuilder;
import org.jboss.msc.service.ServiceBuilder.DependencyType;
import org.jboss.msc.service.ServiceController;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.ServiceTarget;
import org.jboss.msc.value.InjectedValue;
import org.wildfly.clustering.jgroups.spi.ChannelFactory;
import org.wildfly.clustering.jgroups.spi.JGroupsDefaultRequirement;
import org.wildfly.clustering.jgroups.spi.JGroupsRequirement;
import org.wildfly.common.function.ExceptionSupplier;
import org.wildfly.extension.messaging.activemq.jms.JMSService;
import org.wildfly.extension.messaging.activemq.logging.MessagingLogger;
import org.wildfly.security.auth.server.SecurityDomain;
import org.wildfly.security.credential.source.CredentialSource;
/**
* Add handler for a ActiveMQ server instance.
*
* @author Emanuel Muckenhuber
* @author <a href="mailto:andy.taylor@jboss.com">Andy Taylor</a>
* @author Brian Stansberry (c) 2011 Red Hat Inc.
* @author <a href="http://jmesnil.net">Jeff Mesnil</a> (c) 2012 Red Hat Inc.
*/
class ServerAdd extends AbstractAddStepHandler {
static final String PATH_BASE = "paths";
public static final ServerAdd INSTANCE = new ServerAdd();
private ServerAdd() {
super(ACTIVEMQ_SERVER_CAPABILITY, ServerDefinition.ATTRIBUTES);
}
@Override
protected Resource createResource(OperationContext context) {
ActiveMQServerResource resource = new ActiveMQServerResource();
context.addResource(PathAddress.EMPTY_ADDRESS, resource);
return resource;
}
@Override
protected void populateModel(OperationContext context, ModelNode operation, Resource resource) throws OperationFailedException {
super.populateModel(context, operation, resource);
// add an operation to create all the messaging paths resources that have not been already been created
// prior to adding the ActiveMQ server
context.addStep(new OperationStepHandler() {
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
final ModelNode model = Resource.Tools.readModel(resource);
for (String path : PathDefinition.PATHS.keySet()) {
if (!model.get(ModelDescriptionConstants.PATH).hasDefined(path)) {
PathAddress pathAddress = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.PATH, path));
context.createResource(pathAddress);
}
}
}
}, OperationContext.Stage.MODEL);
context.addStep((operationContext, model) -> {
// check that if journal-datasource is defined, no other attributes related to file-system journal are set.
if (ServerDefinition.JOURNAL_DATASOURCE.resolveModelAttribute(context, model).isDefined()) {
checkNoAttributesIsDefined(ServerDefinition.JOURNAL_DATASOURCE.getName(), operationContext.getCurrentAddress(), model,
ServerDefinition.JOURNAL_TYPE,
ServerDefinition.JOURNAL_BUFFER_TIMEOUT,
ServerDefinition.JOURNAL_BUFFER_SIZE,
ServerDefinition.JOURNAL_SYNC_TRANSACTIONAL,
ServerDefinition.JOURNAL_SYNC_NON_TRANSACTIONAL,
ServerDefinition.LOG_JOURNAL_WRITE_RATE,
ServerDefinition.JOURNAL_FILE_SIZE,
ServerDefinition.JOURNAL_MIN_FILES,
ServerDefinition.JOURNAL_POOL_FILES,
ServerDefinition.JOURNAL_COMPACT_PERCENTAGE,
ServerDefinition.JOURNAL_COMPACT_MIN_FILES,
ServerDefinition.JOURNAL_MAX_IO,
ServerDefinition.CREATE_BINDINGS_DIR,
ServerDefinition.CREATE_JOURNAL_DIR);
}
}, OperationContext.Stage.MODEL);
}
/*
* Check that none of the attrs are defined, or log a warning.
*/
private void checkNoAttributesIsDefined(String definedAttributeName, PathAddress address, ModelNode model, AttributeDefinition... attrs) throws OperationFailedException {
List<String> definedAttributes = new ArrayList<>();
for(AttributeDefinition attr : attrs) {
if (model.get(attr.getName()).isDefined()) {
definedAttributes.add(attr.getName());
}
}
if (!definedAttributes.isEmpty()) {
MessagingLogger.ROOT_LOGGER.invalidConfiguration(address, definedAttributeName, definedAttributes);
}
}
@Override
protected void performRuntime(OperationContext context, ModelNode operation, Resource resource) throws OperationFailedException {
// Add a RUNTIME step to actually install the ActiveMQ Service. This will execute after the runtime step
// added by any child resources whose ADD handler executes after this one in the model stage.
context.addStep(new OperationStepHandler() {
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
final ServiceTarget serviceTarget = context.getServiceTarget();
final String serverName = context.getCurrentAddressValue();
// Transform the configuration based on the recursive model
final ModelNode model = Resource.Tools.readModel(resource);
final Configuration configuration = transformConfig(context, serverName, model);
// Create path services
String bindingsPath = PATHS.get(BINDINGS_DIRECTORY).resolveModelAttribute(context, model.get(PATH, BINDINGS_DIRECTORY)).asString();
String bindingsRelativeToPath = RELATIVE_TO.resolveModelAttribute(context, model.get(PATH, BINDINGS_DIRECTORY)).asString();
String journalPath = PATHS.get(JOURNAL_DIRECTORY).resolveModelAttribute(context, model.get(PATH, JOURNAL_DIRECTORY)).asString();
String journalRelativeToPath = RELATIVE_TO.resolveModelAttribute(context, model.get(PATH, JOURNAL_DIRECTORY)).asString();
String largeMessagePath = PATHS.get(LARGE_MESSAGES_DIRECTORY).resolveModelAttribute(context, model.get(PATH, LARGE_MESSAGES_DIRECTORY)).asString();
String largeMessageRelativeToPath = RELATIVE_TO.resolveModelAttribute(context, model.get(PATH, LARGE_MESSAGES_DIRECTORY)).asString();
String pagingPath = PATHS.get(PAGING_DIRECTORY).resolveModelAttribute(context, model.get(PATH, PAGING_DIRECTORY)).asString();
String pagingRelativeToPath = RELATIVE_TO.resolveModelAttribute(context, model.get(PATH, PAGING_DIRECTORY)).asString();
// Create the ActiveMQ Service
final ActiveMQServerService serverService = new ActiveMQServerService(
configuration, new ActiveMQServerService.PathConfig(bindingsPath, bindingsRelativeToPath, journalPath, journalRelativeToPath, largeMessagePath, largeMessageRelativeToPath, pagingPath, pagingRelativeToPath)
);
processIncomingInterceptors(INCOMING_INTERCEPTORS.resolveModelAttribute(context, operation), serverService);
processOutgoingInterceptors(OUTGOING_INTERCEPTORS.resolveModelAttribute(context, operation), serverService);
// Add the ActiveMQ Service
ServiceName activeMQServiceName = MessagingServices.getActiveMQServiceName(serverName);
final ServiceBuilder<ActiveMQServer> serviceBuilder = serviceTarget.addService(activeMQServiceName, serverService);
if (context.hasOptionalCapability(JMX_CAPABILITY, ACTIVEMQ_SERVER_CAPABILITY.getDynamicName(serverName), null)) {
ServiceName jmxCapability = context.getCapabilityServiceName(JMX_CAPABILITY, MBeanServer.class);
serviceBuilder.addDependency(jmxCapability, MBeanServer.class, serverService.getMBeanServer());
}
ModelNode dataSource = JOURNAL_DATASOURCE.resolveModelAttribute(context, model);
if (dataSource.isDefined()) {
ServiceName dataSourceCapability = context.getCapabilityServiceName("org.wildfly.data-source", dataSource.asString(), DataSource.class);
serviceBuilder.addDependency(dataSourceCapability, DataSource.class, serverService.getDataSource());
}
serviceBuilder.addDependency(PathManagerService.SERVICE_NAME, PathManager.class, serverService.getPathManagerInjector());
// Inject a reference to the Elytron security domain if one has been defined.
final ModelNode elytronSecurityDomain = ELYTRON_DOMAIN.resolveModelAttribute(context, model);
if (elytronSecurityDomain.isDefined()) {
ServiceName elytronDomainCapability = context.getCapabilityServiceName(ELYTRON_DOMAIN_CAPABILITY, elytronSecurityDomain.asString(), SecurityDomain.class);
serviceBuilder.addDependency(elytronDomainCapability, SecurityDomain.class, serverService.getElytronDomainInjector());
} else {
// Add legacy security
String domain = SECURITY_DOMAIN.resolveModelAttribute(context, model).asString();
serviceBuilder.addDependency(DependencyType.REQUIRED,
SecurityDomainService.SERVICE_NAME.append(domain),
SecurityDomainContext.class,
serverService.getSecurityDomainContextInjector());
}
// inject credential-references for bridges
addBridgeCredentialStoreReference(serverService, configuration, BridgeDefinition.CREDENTIAL_REFERENCE, context, model, serviceBuilder);
addClusterCredentialStoreReference(serverService, ServerDefinition.CREDENTIAL_REFERENCE, context, model, serviceBuilder);
// Process acceptors and connectors
final Set<String> socketBindings = new HashSet<String>();
TransportConfigOperationHandlers.processAcceptors(context, configuration, model, socketBindings);
// if there is any HTTP acceptor, add a dependency on the http-upgrade-registry service to
// make sure that ActiveMQ server will be stopped *after* the registry (and its underlying XNIO thread)
// is stopped.
if (model.hasDefined(HTTP_ACCEPTOR)) {
for (final Property property : model.get(HTTP_ACCEPTOR).asPropertyList()) {
String httpListener = HTTPAcceptorDefinition.HTTP_LISTENER.resolveModelAttribute(context, property.getValue()).asString();
serviceBuilder.addDependency(MessagingServices.HTTP_UPGRADE_REGISTRY.append(httpListener));
}
}
for (final String socketBinding : socketBindings) {
final ServiceName socketName = SocketBinding.JBOSS_BINDING_NAME.append(socketBinding);
serviceBuilder.addDependency(socketName, SocketBinding.class, serverService.getSocketBindingInjector(socketBinding));
}
final Set<String> outboundSocketBindings = new HashSet<String>();
TransportConfigOperationHandlers.processConnectors(context, configuration, model, outboundSocketBindings);
for (final String outboundSocketBinding : outboundSocketBindings) {
final ServiceName outboundSocketName = OutboundSocketBinding.OUTBOUND_SOCKET_BINDING_BASE_SERVICE_NAME.append(outboundSocketBinding);
// Optional dependency so it won't fail if the user used a ref to socket-binding instead of
// outgoing-socket-binding
serviceBuilder.addDependency(DependencyType.OPTIONAL, outboundSocketName, OutboundSocketBinding.class, serverService.getOutboundSocketBindingInjector(outboundSocketBinding));
if (!socketBindings.contains(outboundSocketBinding)) {
// Add a dependency on the regular socket binding as well so users don't have to use
// outgoing-socket-binding to configure a ref to the local server socket
final ServiceName socketName = SocketBinding.JBOSS_BINDING_NAME.append(outboundSocketBinding);
serviceBuilder.addDependency(DependencyType.OPTIONAL, socketName, SocketBinding.class, serverService.getSocketBindingInjector(outboundSocketBinding));
}
}
//this requires connectors
BroadcastGroupAdd.addBroadcastGroupConfigs(context, configuration, model);
final List<BroadcastGroupConfiguration> broadcastGroupConfigurations = configuration.getBroadcastGroupConfigurations();
final Map<String, DiscoveryGroupConfiguration> discoveryGroupConfigurations = configuration.getDiscoveryGroupConfigurations();
if(broadcastGroupConfigurations != null) {
for(final BroadcastGroupConfiguration config : broadcastGroupConfigurations) {
final String name = config.getName();
final String key = "broadcast" + name;
ModelNode broadcastGroupModel = model.get(BROADCAST_GROUP, name);
if (broadcastGroupModel.hasDefined(JGROUPS_CHANNEL.getName())) {
ModelNode channelFactory = BroadcastGroupDefinition.JGROUPS_STACK.resolveModelAttribute(context, broadcastGroupModel);
ServiceName channelFactoryServiceName = channelFactory.isDefined() ? JGroupsRequirement.CHANNEL_FACTORY.getServiceName(context, channelFactory.asString()) : JGroupsDefaultRequirement.CHANNEL_FACTORY.getServiceName(context);
String channelName = JGROUPS_CHANNEL.resolveModelAttribute(context, broadcastGroupModel).asString();
serviceBuilder.addDependency(channelFactoryServiceName, ChannelFactory.class, serverService.getJGroupsInjector(key));
serverService.getJGroupsChannels().put(key, channelName);
} else {
final ServiceName groupBinding = GroupBindingService.getBroadcastBaseServiceName(activeMQServiceName).append(name);
serviceBuilder.addDependency(groupBinding, SocketBinding.class, serverService.getGroupBindingInjector(key));
}
}
}
if(discoveryGroupConfigurations != null) {
for(final DiscoveryGroupConfiguration config : discoveryGroupConfigurations.values()) {
final String name = config.getName();
final String key = "discovery" + name;
ModelNode discoveryGroupModel = model.get(DISCOVERY_GROUP, name);
if (discoveryGroupModel.hasDefined(JGROUPS_CHANNEL.getName())) {
ModelNode channelFactory = DiscoveryGroupDefinition.JGROUPS_STACK.resolveModelAttribute(context, discoveryGroupModel);
ServiceName channelFactoryServiceName = channelFactory.isDefined() ? JGroupsRequirement.CHANNEL_FACTORY.getServiceName(context, channelFactory.asString()) : JGroupsDefaultRequirement.CHANNEL_FACTORY.getServiceName(context);
String channelName = JGROUPS_CHANNEL.resolveModelAttribute(context, discoveryGroupModel).asString();
serviceBuilder.addDependency(channelFactoryServiceName, ChannelFactory.class, serverService.getJGroupsInjector(key));
serverService.getJGroupsChannels().put(key, channelName);
} else {
final ServiceName groupBinding = GroupBindingService.getDiscoveryBaseServiceName(activeMQServiceName).append(name);
serviceBuilder.addDependency(groupBinding, SocketBinding.class, serverService.getGroupBindingInjector(key));
}
}
}
// Install the ActiveMQ Service
ServiceController<ActiveMQServer> activeMQServerServiceController = serviceBuilder.install();
// Provide our custom Resource impl a ref to the ActiveMQ server so it can create child runtime resources
((ActiveMQServerResource)resource).setActiveMQServerServiceController(activeMQServerServiceController);
// Install the JMSService
boolean overrideInVMSecurity = OVERRIDE_IN_VM_SECURITY.resolveModelAttribute(context, operation).asBoolean();
JMSService.addService(serviceTarget, activeMQServiceName, overrideInVMSecurity);
context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
}
}, OperationContext.Stage.RUNTIME);
}
/**
* Transform the detyped operation parameters into the ActiveMQ configuration.
*
* @param context the operation context
* @param serverName the name of the ActiveMQ instance
* @param model the subsystem root resource model
* @return the ActiveMQ configuration
*/
private Configuration transformConfig(final OperationContext context, String serverName, final ModelNode model) throws OperationFailedException {
Configuration configuration = new ConfigurationImpl();
configuration.setName(serverName);
configuration.setEnabledAsyncConnectionExecution(ASYNC_CONNECTION_EXECUTION_ENABLED.resolveModelAttribute(context, model).asBoolean());
configuration.setClusterPassword(CLUSTER_PASSWORD.resolveModelAttribute(context, model).asString());
configuration.setClusterUser(CLUSTER_USER.resolveModelAttribute(context, model).asString());
configuration.setConnectionTTLOverride(CONNECTION_TTL_OVERRIDE.resolveModelAttribute(context, model).asInt());
configuration.setCreateBindingsDir(CREATE_BINDINGS_DIR.resolveModelAttribute(context, model).asBoolean());
configuration.setCreateJournalDir(CREATE_JOURNAL_DIR.resolveModelAttribute(context, model).asBoolean());
configuration.setIDCacheSize(ID_CACHE_SIZE.resolveModelAttribute(context, model).asInt());
// TODO do we want to allow the jmx configuration ?
configuration.setJMXDomain(JMX_DOMAIN.resolveModelAttribute(context, model).asString());
configuration.setJMXManagementEnabled(JMX_MANAGEMENT_ENABLED.resolveModelAttribute(context, model).asBoolean());
// Journal
final JournalType journalType = JournalType.valueOf(JOURNAL_TYPE.resolveModelAttribute(context, model).asString());
configuration.setJournalType(journalType);
// AIO Journal
configuration.setJournalBufferSize_AIO(JOURNAL_BUFFER_SIZE.resolveModelAttribute(context, model).asInt(ActiveMQDefaultConfiguration.getDefaultJournalBufferSizeAio()));
configuration.setJournalBufferTimeout_AIO(JOURNAL_BUFFER_TIMEOUT.resolveModelAttribute(context, model).asInt(ActiveMQDefaultConfiguration.getDefaultJournalBufferTimeoutAio()));
configuration.setJournalMaxIO_AIO(JOURNAL_MAX_IO.resolveModelAttribute(context, model).asInt(ActiveMQDefaultConfiguration.getDefaultJournalMaxIoAio()));
// NIO Journal
configuration.setJournalBufferSize_NIO(JOURNAL_BUFFER_SIZE.resolveModelAttribute(context, model).asInt(ActiveMQDefaultConfiguration.getDefaultJournalBufferSizeNio()));
configuration.setJournalBufferTimeout_NIO(JOURNAL_BUFFER_TIMEOUT.resolveModelAttribute(context, model).asInt(ActiveMQDefaultConfiguration.getDefaultJournalBufferTimeoutNio()));
configuration.setJournalMaxIO_NIO(JOURNAL_MAX_IO.resolveModelAttribute(context, model).asInt(ActiveMQDefaultConfiguration.getDefaultJournalMaxIoNio()));
//
configuration.setJournalCompactMinFiles(JOURNAL_COMPACT_MIN_FILES.resolveModelAttribute(context, model).asInt());
configuration.setJournalCompactPercentage(JOURNAL_COMPACT_PERCENTAGE.resolveModelAttribute(context, model).asInt());
configuration.setJournalFileSize(JOURNAL_FILE_SIZE.resolveModelAttribute(context, model).asInt());
configuration.setJournalMinFiles(JOURNAL_MIN_FILES.resolveModelAttribute(context, model).asInt());
configuration.setJournalPoolFiles(JOURNAL_POOL_FILES.resolveModelAttribute(context, model).asInt());
configuration.setJournalSyncNonTransactional(JOURNAL_SYNC_NON_TRANSACTIONAL.resolveModelAttribute(context, model).asBoolean());
configuration.setJournalSyncTransactional(JOURNAL_SYNC_TRANSACTIONAL.resolveModelAttribute(context, model).asBoolean());
configuration.setLogJournalWriteRate(LOG_JOURNAL_WRITE_RATE.resolveModelAttribute(context, model).asBoolean());
configuration.setManagementAddress(SimpleString.toSimpleString(MANAGEMENT_ADDRESS.resolveModelAttribute(context, model).asString()));
configuration.setManagementNotificationAddress(SimpleString.toSimpleString(MANAGEMENT_NOTIFICATION_ADDRESS.resolveModelAttribute(context, model).asString()));
configuration.setMemoryMeasureInterval(MEMORY_MEASURE_INTERVAL.resolveModelAttribute(context, model).asLong());
configuration.setMemoryWarningThreshold(MEMORY_WARNING_THRESHOLD.resolveModelAttribute(context, model).asInt());
configuration.setMessageCounterEnabled(STATISTICS_ENABLED.resolveModelAttribute(context, model).asBoolean());
configuration.setMessageCounterSamplePeriod(MESSAGE_COUNTER_SAMPLE_PERIOD.resolveModelAttribute(context, model).asInt());
configuration.setMessageCounterMaxDayHistory(MESSAGE_COUNTER_MAX_DAY_HISTORY.resolveModelAttribute(context, model).asInt());
configuration.setMessageExpiryScanPeriod(MESSAGE_EXPIRY_SCAN_PERIOD.resolveModelAttribute(context, model).asLong());
configuration.setMessageExpiryThreadPriority(MESSAGE_EXPIRY_THREAD_PRIORITY.resolveModelAttribute(context, model).asInt());
configuration.setJournalPerfBlastPages(PERF_BLAST_PAGES.resolveModelAttribute(context, model).asInt());
configuration.setPersistDeliveryCountBeforeDelivery(PERSIST_DELIVERY_COUNT_BEFORE_DELIVERY.resolveModelAttribute(context, model).asBoolean());
configuration.setPageMaxConcurrentIO(PAGE_MAX_CONCURRENT_IO.resolveModelAttribute(context, model).asInt());
configuration.setPersistenceEnabled(PERSISTENCE_ENABLED.resolveModelAttribute(context, model).asBoolean());
configuration.setPersistIDCache(PERSIST_ID_CACHE.resolveModelAttribute(context, model).asBoolean());
configuration.setRunSyncSpeedTest(RUN_SYNC_SPEED_TEST.resolveModelAttribute(context, model).asBoolean());
configuration.setScheduledThreadPoolMaxSize(SCHEDULED_THREAD_POOL_MAX_SIZE.resolveModelAttribute(context, model).asInt());
configuration.setSecurityEnabled(SECURITY_ENABLED.resolveModelAttribute(context, model).asBoolean());
configuration.setSecurityInvalidationInterval(SECURITY_INVALIDATION_INTERVAL.resolveModelAttribute(context, model).asLong());
configuration.setServerDumpInterval(SERVER_DUMP_INTERVAL.resolveModelAttribute(context, model).asLong());
configuration.setThreadPoolMaxSize(THREAD_POOL_MAX_SIZE.resolveModelAttribute(context, model).asInt());
configuration.setTransactionTimeout(TRANSACTION_TIMEOUT.resolveModelAttribute(context, model).asLong());
configuration.setTransactionTimeoutScanPeriod(TRANSACTION_TIMEOUT_SCAN_PERIOD.resolveModelAttribute(context, model).asLong());
configuration.setWildcardRoutingEnabled(WILD_CARD_ROUTING_ENABLED.resolveModelAttribute(context, model).asBoolean());
processStorageConfiguration(context, model, configuration);
addHAPolicyConfiguration(context, configuration, model);
processAddressSettings(context, configuration, model);
processSecuritySettings(context, configuration, model);
// Add in items from child resources
GroupingHandlerAdd.addGroupingHandlerConfig(context,configuration, model);
DiscoveryGroupAdd.addDiscoveryGroupConfigs(context, configuration, model);
DivertAdd.addDivertConfigs(context, configuration, model);
QueueAdd.addQueueConfigs(context, configuration, model);
BridgeAdd.addBridgeConfigs(context, configuration, model);
ClusterConnectionAdd.addClusterConnectionConfigs(context, configuration, model);
ConnectorServiceDefinition.addConnectorServiceConfigs(context, configuration, model);
return configuration;
}
private static void processStorageConfiguration(OperationContext context, ModelNode model, Configuration configuration) throws OperationFailedException {
ModelNode journalDataSource = JOURNAL_DATASOURCE.resolveModelAttribute(context, model);
if (!journalDataSource.isDefined()) {
return;
}
DatabaseStorageConfiguration storageConfiguration = new DatabaseStorageConfiguration();
storageConfiguration.setBindingsTableName(JOURNAL_BINDINGS_TABLE.resolveModelAttribute(context, model).asString());
storageConfiguration.setJMSBindingsTableName(JOURNAL_JMS_BINDINGS_TABLE.resolveModelAttribute(context, model).asString());
storageConfiguration.setMessageTableName(JOURNAL_MESSAGES_TABLE.resolveModelAttribute(context, model).asString());
storageConfiguration.setLargeMessageTableName(JOURNAL_LARGE_MESSAGES_TABLE.resolveModelAttribute(context, model).asString());
storageConfiguration.setPageStoreTableName(JOURNAL_PAGE_STORE_TABLE.resolveModelAttribute(context, model).asString());
storageConfiguration.setJdbcNetworkTimeout(JOURNAL_JDBC_NETWORK_TIMEOUT.resolveModelAttribute(context, model).asInt());
ModelNode databaseNode = JOURNAL_DATABASE.resolveModelAttribute(context, model);
final String database = databaseNode.isDefined() ? databaseNode.asString() : null;
try {
storageConfiguration.setSqlProvider(new PropertySQLProviderFactory(database));
} catch (IOException e) {
throw new OperationFailedException(e);
}
configuration.setStoreConfiguration(storageConfiguration);
}
/**
* Process the address settings.
*
* @param configuration the ActiveMQ configuration
* @param params the detyped operation parameters
*/
/**
* Process the address settings.
*
* @param configuration the ActiveMQ configuration
* @param params the detyped operation parameters
* @throws org.jboss.as.controller.OperationFailedException
*/
static void processAddressSettings(final OperationContext context, final Configuration configuration, final ModelNode params) throws OperationFailedException {
if (params.hasDefined(ADDRESS_SETTING)) {
for (final Property property : params.get(ADDRESS_SETTING).asPropertyList()) {
final String match = property.getName();
final ModelNode config = property.getValue();
final AddressSettings settings = AddressSettingAdd.createSettings(context, config);
configuration.getAddressesSettings().put(match, settings);
}
}
}
private List<Class> unwrapClasses(List<ModelNode> classesModel) throws OperationFailedException {
List<Class> classes = new ArrayList<>();
for (ModelNode classModel : classesModel) {
Class<?> clazz = unwrapClass(classModel);
classes.add(clazz);
}
return classes;
}
private static Class unwrapClass(ModelNode classModel) throws OperationFailedException {
String className = classModel.get(NAME).asString();
String moduleName = classModel.get(MODULE).asString();
try {
ModuleIdentifier moduleID = ModuleIdentifier.fromString(moduleName);
Module module = Module.getCallerModuleLoader().loadModule(moduleID);
Class<?> clazz = module.getClassLoader().loadClass(className);
return clazz;
} catch (Exception e) {
throw MessagingLogger.ROOT_LOGGER.unableToLoadClassFromModule(className, moduleName);
}
}
private void processIncomingInterceptors(ModelNode model, ActiveMQServerService serverService) throws OperationFailedException {
if (!model.isDefined()) {
return;
}
List<ModelNode> interceptors = model.asList();
for (Class clazz : unwrapClasses(interceptors)) {
try {
Interceptor interceptor = Interceptor.class.cast(clazz.newInstance());
serverService.getIncomingInterceptors().add(interceptor);
} catch (Exception e) {
throw new OperationFailedException(e);
}
}
}
private void processOutgoingInterceptors(ModelNode model, ActiveMQServerService serverService) throws OperationFailedException {
if (!model.isDefined()) {
return;
}
List<ModelNode> interceptors = model.asList();
for (Class clazz : unwrapClasses(interceptors)) {
try {
Interceptor interceptor = Interceptor.class.cast(clazz.newInstance());
serverService.getOutgoingInterceptors().add(interceptor);
} catch (Exception e) {
throw new OperationFailedException(e);
}
}
}
/**
* Process the security settings.
*
* @param configuration the ActiveMQ configuration
* @param params the detyped operation parameters
*/
static void processSecuritySettings(final OperationContext context, final Configuration configuration, final ModelNode params) throws OperationFailedException {
if (params.get(SECURITY_SETTING).isDefined()) {
for (final Property property : params.get(SECURITY_SETTING).asPropertyList()) {
final String match = property.getName();
final ModelNode config = property.getValue();
if(config.hasDefined(CommonAttributes.ROLE)) {
final Set<Role> roles = new HashSet<Role>();
for (final Property role : config.get(CommonAttributes.ROLE).asPropertyList()) {
roles.add(SecurityRoleDefinition.transform(context, role.getName(), role.getValue()));
}
configuration.getSecurityRoles().put(match, roles);
}
}
}
}
private static void addBridgeCredentialStoreReference(ActiveMQServerService amqService, Configuration configuration, ObjectTypeAttributeDefinition credentialReferenceAttributeDefinition, OperationContext context, ModelNode model, ServiceBuilder<?> serviceBuilder) throws OperationFailedException {
for (BridgeConfiguration bridgeConfiguration: configuration.getBridgeConfigurations()) {
String name = bridgeConfiguration.getName();
InjectedValue<ExceptionSupplier<CredentialSource, Exception>> injector = amqService.getBridgeCredentialSourceSupplierInjector(name);
String[] modelFilter = { CommonAttributes.BRIDGE, name };
ModelNode filteredModelNode = model;
if (modelFilter != null && modelFilter.length > 0) {
for (String path : modelFilter) {
if (filteredModelNode.get(path).isDefined())
filteredModelNode = filteredModelNode.get(path);
else
break;
}
}
ModelNode value = credentialReferenceAttributeDefinition.resolveModelAttribute(context, filteredModelNode);
if (value.isDefined()) {
injector.inject(CredentialReference.getCredentialSourceSupplier(context, credentialReferenceAttributeDefinition, filteredModelNode, serviceBuilder));
}
}
}
private static void addClusterCredentialStoreReference(ActiveMQServerService amqService, ObjectTypeAttributeDefinition credentialReferenceAttributeDefinition, OperationContext context, ModelNode model, ServiceBuilder<?> serviceBuilder) throws OperationFailedException {
ModelNode value = credentialReferenceAttributeDefinition.resolveModelAttribute(context, model);
if (value.isDefined()) {
amqService.getClusterCredentialSourceSupplierInjector()
.inject(CredentialReference.getCredentialSourceSupplier(context, credentialReferenceAttributeDefinition, model, serviceBuilder));
}
}
}