/* * JBoss, Home of Professional Open Source. * Copyright 2015, 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.messaging; import static java.util.Arrays.asList; import static org.jboss.as.controller.OperationContext.Stage.MODEL; import static org.jboss.as.controller.PathAddress.pathAddress; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.EXTENSION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILURE_DESCRIPTION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.MODULE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE; import static org.jboss.as.controller.operations.common.Util.createAddOperation; import static org.jboss.as.controller.operations.common.Util.createOperation; import static org.jboss.as.controller.operations.common.Util.createRemoveOperation; import static org.jboss.as.messaging.CommonAttributes.ACCEPTOR; import static org.jboss.as.messaging.CommonAttributes.ADDRESS; import static org.jboss.as.messaging.CommonAttributes.ALLOW_FAILBACK; import static org.jboss.as.messaging.CommonAttributes.BACKUP; import static org.jboss.as.messaging.CommonAttributes.BACKUP_GROUP_NAME; import static org.jboss.as.messaging.CommonAttributes.BRIDGE; import static org.jboss.as.messaging.CommonAttributes.BROADCAST_GROUP; import static org.jboss.as.messaging.CommonAttributes.CHECK_FOR_LIVE_SERVER; import static org.jboss.as.messaging.CommonAttributes.CLUSTER_CONNECTION; import static org.jboss.as.messaging.CommonAttributes.CONNECTION_FACTORY; import static org.jboss.as.messaging.CommonAttributes.CONNECTOR; import static org.jboss.as.messaging.CommonAttributes.CONNECTOR_REF_STRING; import static org.jboss.as.messaging.CommonAttributes.CONNECTOR_SERVICE; import static org.jboss.as.messaging.CommonAttributes.DISCOVERY_GROUP; import static org.jboss.as.messaging.CommonAttributes.DISCOVERY_GROUP_NAME; import static org.jboss.as.messaging.CommonAttributes.ENTRIES; import static org.jboss.as.messaging.CommonAttributes.FACTORY_CLASS; import static org.jboss.as.messaging.CommonAttributes.FAILBACK_DELAY; import static org.jboss.as.messaging.CommonAttributes.FAILOVER_ON_SHUTDOWN; import static org.jboss.as.messaging.CommonAttributes.GROUP_ADDRESS; import static org.jboss.as.messaging.CommonAttributes.GROUP_PORT; import static org.jboss.as.messaging.CommonAttributes.HORNETQ_SERVER; import static org.jboss.as.messaging.CommonAttributes.HTTP_ACCEPTOR; import static org.jboss.as.messaging.CommonAttributes.HTTP_CONNECTOR; import static org.jboss.as.messaging.CommonAttributes.JGROUPS_CHANNEL; import static org.jboss.as.messaging.CommonAttributes.JGROUPS_STACK; import static org.jboss.as.messaging.CommonAttributes.JMS_QUEUE; import static org.jboss.as.messaging.CommonAttributes.JMS_TOPIC; import static org.jboss.as.messaging.CommonAttributes.LOCAL_BIND_ADDRESS; import static org.jboss.as.messaging.CommonAttributes.LOCAL_BIND_PORT; import static org.jboss.as.messaging.CommonAttributes.MAX_SAVED_REPLICATED_JOURNAL_SIZE; import static org.jboss.as.messaging.CommonAttributes.POOLED_CONNECTION_FACTORY; import static org.jboss.as.messaging.CommonAttributes.REMOTE_ACCEPTOR; import static org.jboss.as.messaging.CommonAttributes.REMOTE_CONNECTOR; import static org.jboss.as.messaging.CommonAttributes.SHARED_STORE; import static org.jboss.as.messaging.logging.MessagingLogger.ROOT_LOGGER; import static org.jboss.dmr.ModelType.BOOLEAN; import static org.jboss.dmr.ModelType.EXPRESSION; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.jboss.as.controller.AttributeDefinition; 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.ProcessType; import org.jboss.as.controller.RunningMode; import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; import org.jboss.as.controller.SimpleMapAttributeDefinition; import org.jboss.as.controller.SimpleOperationDefinitionBuilder; import org.jboss.as.controller.StringListAttributeDefinition; import org.jboss.as.controller.access.management.SensitiveTargetAccessConstraintDefinition; import org.jboss.as.controller.descriptions.ResourceDescriptionResolver; import org.jboss.as.controller.operations.MultistepUtil; import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler; import org.jboss.as.controller.registry.ManagementResourceRegistration; import org.jboss.as.controller.registry.OperationEntry; import org.jboss.as.controller.registry.Resource; import org.jboss.as.messaging.logging.MessagingLogger; import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelType; import org.jboss.dmr.Property; /** * Operation to migrate from the legacy messaging subsystem to the new messaging-activemq subsystem. * * This operation must be performed when the server is in admin-only mode. * Internally, the operation: * * <ul> * <li>query the description of all the messaging subsystem by invoking the :describe operation. * This returns a list of :add operations for each messaging resources.</li> * <li>:add the new org.widlfy.extension.messaging-activemq extension</li> * <li>for each messaging resources, transform the :add operations to add the * corresponding resource to the new messaging-activemq subsystem. * In this step, changes to the resources model are taken into account * (e.g. cluster-connection's connector-ref is named connector-name in the new messaging-activemq subsystem.)</li> * <li>:remove the messaging subsystem</li> * </ul> * * The companion <code>:describe-migration</code> operation will return a list of all the actual operations that would be * performed during the invocation of the <code>:migrate</code> operation. * * @author <a href="http://jmesnil.net/">Jeff Mesnil</a> (c) 2015 Red Hat inc. */ public class MigrateOperation implements OperationStepHandler { private static final String MESSAGING_ACTIVEMQ_EXTENSION = "org.wildfly.extension.messaging-activemq"; private static final String MESSAGING_ACTIVEMQ_MODULE = "org.wildfly.extension.messaging-activemq"; private static final PathAddress MESSAGING_EXTENSION = pathAddress(PathElement.pathElement(EXTENSION, "org.jboss.as.messaging")); private static final String NEW_ENTRY_SUFFIX = "-new"; private static final String HORNETQ_NETTY_CONNECTOR_FACTORY = "org.hornetq.core.remoting.impl.netty.NettyConnectorFactory"; private static final String HORNETQ_NETTY_ACCEPTOR_FACTORY = "org.hornetq.core.remoting.impl.netty.NettyAcceptorFactory"; private static final String ARTEMIS_NETTY_CONNECTOR_FACTORY = "org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory"; private static final String ARTEMIS_NETTY_ACCEPTOR_FACTORY = "org.apache.activemq.artemis.core.remoting.impl.netty.NettyAcceptorFactory"; public static final String MIGRATE = "migrate"; public static final String MIGRATION_WARNINGS = "migration-warnings"; public static final String MIGRATION_ERROR = "migration-error"; public static final String MIGRATION_OPERATIONS = "migration-operations"; public static final String DESCRIBE_MIGRATION = "describe-migration"; public static final StringListAttributeDefinition MIGRATION_WARNINGS_ATTR = new StringListAttributeDefinition.Builder(MIGRATION_WARNINGS) .setRequired(false) .build(); public static final SimpleMapAttributeDefinition MIGRATION_ERROR_ATTR = new SimpleMapAttributeDefinition.Builder(MIGRATION_ERROR, ModelType.OBJECT, true) .setValueType(ModelType.OBJECT) .setRequired(false) .build(); private static final OperationStepHandler DESCRIBE_MIGRATION_INSTANCE = new MigrateOperation(true); private static final OperationStepHandler MIGRATE_INSTANCE = new MigrateOperation(false); private static final AttributeDefinition ADD_LEGACY_ENTRIES = SimpleAttributeDefinitionBuilder.create("add-legacy-entries", BOOLEAN) .setDefaultValue(new ModelNode(false)) .build(); public static final String HA_POLICY = "ha-policy"; private final boolean describe; private MigrateOperation(boolean describe) { this.describe = describe; } static void registerOperations(ManagementResourceRegistration registry, ResourceDescriptionResolver resourceDescriptionResolver) { registry.registerOperationHandler(new SimpleOperationDefinitionBuilder(MIGRATE, resourceDescriptionResolver) .setParameters(ADD_LEGACY_ENTRIES) .setReplyParameters(MIGRATION_WARNINGS_ATTR, MIGRATION_ERROR_ATTR) .setRuntimeOnly() .setAccessConstraints(SensitiveTargetAccessConstraintDefinition.READ_WHOLE_CONFIG) .build(), MigrateOperation.MIGRATE_INSTANCE); registry.registerOperationHandler(new SimpleOperationDefinitionBuilder(DESCRIBE_MIGRATION, resourceDescriptionResolver) .addParameter(ADD_LEGACY_ENTRIES) .setReplyParameters(MIGRATION_WARNINGS_ATTR) .setRuntimeOnly() .setAccessConstraints(SensitiveTargetAccessConstraintDefinition.READ_WHOLE_CONFIG) .build(), MigrateOperation.DESCRIBE_MIGRATION_INSTANCE); } @Override public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { if (!describe && context.getRunningMode() != RunningMode.ADMIN_ONLY) { throw ROOT_LOGGER.migrateOperationAllowedOnlyInAdminOnly(); } boolean addLegacyEntries = ADD_LEGACY_ENTRIES.resolveModelAttribute(context, operation).asBoolean(); final List<String> warnings = new ArrayList<>(); // node containing the description (list of add operations) of the legacy subsystem final ModelNode legacyModelAddOps = new ModelNode(); // preserve the order of insertion of the add operations for the new subsystem. final Map<PathAddress, ModelNode> migrationOperations = new LinkedHashMap<PathAddress, ModelNode>(); // invoke an OSH to describe the legacy messaging subsystem describeLegacyMessagingResources(context, legacyModelAddOps); // invoke an OSH to add the messaging-activemq extension // FIXME: this does not work it the extension :add is added to the migrationOperations directly (https://issues.jboss.org/browse/WFCORE-323) addMessagingActiveMQExtension(context, migrationOperations, describe); context.addStep(new OperationStepHandler() { @Override public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { // transform the legacy add operations and put them in migrationOperations transformResources(context, legacyModelAddOps, migrationOperations, addLegacyEntries, warnings); // put the /subsystem=messaging:remove operation removeMessagingSubsystem(migrationOperations, context.getProcessType() == ProcessType.STANDALONE_SERVER); PathAddress parentAddress = context.getCurrentAddress().getParent(); fixAddressesForDomainMode(parentAddress, migrationOperations); if (describe) { // :describe-migration operation // for describe-migration operation, do nothing and return the list of operations that would // be executed in the composite operation final Collection<ModelNode> values = migrationOperations.values(); ModelNode result = new ModelNode(); fillWarnings(result, warnings); result.get(MIGRATION_OPERATIONS).set(values); context.getResult().set(result); } else { // :migrate operation // invoke an OSH on a composite operation with all the migration operations final Map<PathAddress, ModelNode> migrateOpResponses = migrateSubsystems(context, migrationOperations); context.completeStep(new OperationContext.ResultHandler() { @Override public void handleResult(OperationContext.ResultAction resultAction, OperationContext context, ModelNode operation) { final ModelNode result = new ModelNode(); fillWarnings(result, warnings); if (resultAction == OperationContext.ResultAction.ROLLBACK) { for (Map.Entry<PathAddress, ModelNode> entry : migrateOpResponses.entrySet()) { if (entry.getValue().hasDefined(FAILURE_DESCRIPTION)) { //we check for failure description, as every node has 'failed', but one //the real error has a failure description //we break when we find the first one, as there will only ever be one failure //as the op stops after the first failure ModelNode desc = new ModelNode(); desc.get(OP).set(migrationOperations.get(entry.getKey())); desc.get(RESULT).set(entry.getValue()); result.get(MIGRATION_ERROR).set(desc); break; } } context.getFailureDescription().set(ROOT_LOGGER.migrationFailed()); } context.getResult().set(result); } }); } } }, MODEL); } protected void fillWarnings(ModelNode result, List<String> warnings) { ModelNode rw = new ModelNode().setEmptyList(); for (String warning : warnings) { rw.add(warning); } result.get(MIGRATION_WARNINGS).set(rw); } /** * In domain mode, the subsystem are under /profile=XXX. * This method fixes the address by prepending the addresses (that start with /subsystem) with the current * operation parent so that is works both in standalone (parent = EMPTY_ADDRESS) and domain mode * (parent = /profile=XXX) */ private void fixAddressesForDomainMode(PathAddress parentAddress, Map<PathAddress, ModelNode> migrationOperations) { // in standalone mode, do nothing if (parentAddress.size() == 0) { return; } // use a linked hash map to preserve operations order Map<PathAddress, ModelNode> fixedMigrationOperations = new LinkedHashMap<>(migrationOperations); migrationOperations.clear(); for (Map.Entry<PathAddress, ModelNode> entry : fixedMigrationOperations.entrySet()) { PathAddress fixedAddress = parentAddress.append(entry.getKey()); entry.getValue().get(ADDRESS).set(fixedAddress.toModelNode()); migrationOperations.put(fixedAddress, entry.getValue()); } } /** * It's possible that the extension is already present. In that case, this method does nothing. */ private void addMessagingActiveMQExtension(OperationContext context, Map<PathAddress, ModelNode> migrationOperations, boolean describe) { Resource root = context.readResourceFromRoot(PathAddress.EMPTY_ADDRESS, false); if (root.getChildrenNames(EXTENSION).contains(MESSAGING_ACTIVEMQ_EXTENSION)) { // extension is already added, do nothing return; } PathAddress extensionAddress = pathAddress(EXTENSION, MESSAGING_ACTIVEMQ_EXTENSION); OperationEntry addEntry = context.getRootResourceRegistration().getOperationEntry(extensionAddress, ADD); ModelNode addOperation = createAddOperation(extensionAddress); addOperation.get(MODULE).set(MESSAGING_ACTIVEMQ_MODULE); if (describe) { migrationOperations.put(extensionAddress, addOperation); } else { context.addStep(context.getResult().get(extensionAddress.toString()), addOperation, addEntry.getOperationHandler(), MODEL); } } private void removeMessagingSubsystem(Map<PathAddress, ModelNode> migrationOperations, boolean standalone) { PathAddress subsystemAddress = pathAddress(MessagingExtension.SUBSYSTEM_PATH); ModelNode removeOperation = createRemoveOperation(subsystemAddress); migrationOperations.put(subsystemAddress, removeOperation); if(standalone) { removeOperation = createRemoveOperation(MESSAGING_EXTENSION); migrationOperations.put(MESSAGING_EXTENSION, removeOperation); } } private Map<PathAddress, ModelNode> migrateSubsystems(OperationContext context, final Map<PathAddress, ModelNode> migrationOperations) throws OperationFailedException { final Map<PathAddress, ModelNode> result = new LinkedHashMap<>(); MultistepUtil.recordOperationSteps(context, migrationOperations, result); return result; } private ModelNode transformAddress(ModelNode legacyAddress) { ModelNode newAddress = new ModelNode(); for (Property segment : legacyAddress.asPropertyList()) { final Property newSegment; switch (segment.getName()) { case CommonAttributes.SUBSYSTEM: newSegment = new Property(SUBSYSTEM, new ModelNode("messaging-activemq")); break; case HORNETQ_SERVER: newSegment = new Property("server", segment.getValue()); break; default: newSegment = segment; } newAddress.add(newSegment); } return newAddress; } private void transformResources(OperationContext context, final ModelNode legacyModelDescription, final Map<PathAddress, ModelNode> newAddOperations, boolean addLegacyEntries, List<String> warnings) throws OperationFailedException { for (ModelNode legacyAddOp : legacyModelDescription.get(RESULT).asList()) { final ModelNode newAddOp = legacyAddOp.clone(); ModelNode legacyAddress = legacyAddOp.get(OP_ADDR); ModelNode newAddress = transformAddress(legacyAddress.clone()); newAddOp.get(OP_ADDR).set(newAddress); PathAddress address = PathAddress.pathAddress(newAddress); // migrate server resource if (address.size() == 2 && "server".equals(address.getLastElement().getKey())) { migrateServer(PathAddress.pathAddress(legacyAddress), newAddOp, newAddOperations, warnings); continue; } if (newAddress.asList().size() > 2) { // element 0 is subsystem=messaging-activemq String parentType = address.getElement(1).getKey(); String resourceType = address.getElement(2).getKey(); if ("server".equals(parentType)) { switch (resourceType) { case BROADCAST_GROUP: migrateBroadcastGroup(newAddOp, warnings); break; case DISCOVERY_GROUP: migrateDiscoveryGroup(newAddOp, warnings); break; case CONNECTION_FACTORY: if (addLegacyEntries) { if(connectionFactoryIsUsingInVMConnectors(context, legacyAddOp)) { warnings.add(ROOT_LOGGER.couldNotCreateLegacyConnectionFactoryUsingInVMConnector(address)); } else { PathAddress legacyConnectionFactoryAddress = address.getParent().append("legacy-connection-factory", address.getLastElement().getValue()); final ModelNode addLegacyConnectionFactoryOp = legacyAddOp.clone(); addLegacyConnectionFactoryOp.get(OP_ADDR).set(legacyConnectionFactoryAddress.toModelNode()); migrateConnectionFactory(addLegacyConnectionFactoryOp, ""); newAddOperations.put(legacyConnectionFactoryAddress, addLegacyConnectionFactoryOp); } } migrateConnectionFactory(newAddOp, addLegacyEntries ? NEW_ENTRY_SUFFIX : ""); break; case POOLED_CONNECTION_FACTORY: migratePooledConnectionFactory(newAddOp); break; case CLUSTER_CONNECTION: migrateClusterConnection(newAddOp, warnings); break; case BRIDGE: migrateBridge(newAddOp); break; case JMS_QUEUE: case JMS_TOPIC: if (addLegacyEntries) { addLegacyEntries(newAddOp); } break; case ACCEPTOR: case CONNECTOR: migrateGenericTransport(newAddOp); // no break on purpose, the acceptor and connector must also migrate their params. case HTTP_ACCEPTOR: case REMOTE_ACCEPTOR: case HTTP_CONNECTOR: case REMOTE_CONNECTOR: case CONNECTOR_SERVICE: if (address.size() == 4) { // if there are any param resource underneath connectors, acceptors, and connector-services // add them directly to their parent add operation in their params attribute String name = address.getLastElement().getValue(); ModelNode value = newAddOp.get(VALUE); ModelNode parentAddOp = newAddOperations.get(address.getParent()); if (name.equals("http-upgrade-endpoint") && address.getParent().getLastElement().getKey().equals("http-connector")) { parentAddOp.get("endpoint").set(value); } else { if (parameterIsAllowed(name, resourceType)) { parentAddOp.get("params").add(new Property(name, value)); } else { warnings.add(ROOT_LOGGER.couldNotMigrateUnsupportedAttribute(name, address.getParent())); } } continue; } break; } } } newAddOperations.put(address, newAddOp); } } /** * Check if the name of the parameter is allowed for the given resourceType. */ private boolean parameterIsAllowed(String name, String resourceType) { switch (resourceType) { case REMOTE_ACCEPTOR: case HTTP_ACCEPTOR: case REMOTE_CONNECTOR: case HTTP_CONNECTOR: // WFLY-5667 - for now remove only use-nio. Revisit this code when Artemis offers an API // to know which parameters are ignored. if ("use-nio".equals(name)) { return false; } else { return true; } default: // accept any parameter for other resources. return true; } } private boolean connectionFactoryIsUsingInVMConnectors(OperationContext context, ModelNode connectionFactoryAddOp) { ModelNode connector = connectionFactoryAddOp.get(CONNECTOR); if (connector.isDefined()) { PathAddress connectionFactoryAddress = pathAddress(connectionFactoryAddOp.get(OP_ADDR)); PathElement relativeLegacyServerAddress = connectionFactoryAddress.getParent().getLastElement(); // read the server resource related to the context current address (which is the messaging subsystem address). Resource serverResource = context.readResource(pathAddress(relativeLegacyServerAddress), false); Set<String> definedInVMConnectors = serverResource.getChildrenNames("in-vm-connector"); // legacy connector is a property list where the name is the connector and the value is undefined List<Property> connectorProps = connector.asPropertyList(); for (Property connectorProp : connectorProps) { String connectorName = connectorProp.getName(); if (definedInVMConnectors.contains(connectorName)) { return true; } } } return false; } private void migrateDiscoveryGroup(ModelNode newAddOp, List<String> warnings) { // These attributes are not present in the messaging-activemq subsystem. // Instead a socket-binding must be used to configure the broadcast-group. for (String property : asList(LOCAL_BIND_ADDRESS.getName(), GROUP_ADDRESS.getName(), GROUP_PORT.getName())) { if (newAddOp.has(property)) { newAddOp.remove(property); warnings.add(ROOT_LOGGER.couldNotMigrateDiscoveryGroupAttribute(property, pathAddress(newAddOp.get(OP_ADDR)))); } } // These attributes no longer accept expressions in the messaging-activemq subsystem. removePropertiesWithExpression(newAddOp, warnings, JGROUPS_CHANNEL.getName(), JGROUPS_STACK.getName()); } private void migrateBroadcastGroup(ModelNode newAddOp, List<String> warnings) { // These attributes are not present in the messaging-activemq subsystem. // Instead a socket-binding must be used to configure the broadcast-group. final Collection<String> unmigratedProperties = asList(LOCAL_BIND_ADDRESS.getName(), LOCAL_BIND_PORT.getName(), GROUP_ADDRESS.getName(), GROUP_PORT.getName()); for (Property property : newAddOp.asPropertyList()) { if (unmigratedProperties.contains(property.getName())) { warnings.add(ROOT_LOGGER.couldNotMigrateBroadcastGroupAttribute(property.getName(), pathAddress(newAddOp.get(OP_ADDR)))); } } // These attributes no longer accept expressions in the messaging-activemq subsystem. removePropertiesWithExpression(newAddOp, warnings, JGROUPS_CHANNEL.getName(), JGROUPS_STACK.getName()); } private void removePropertiesWithExpression(ModelNode newAddOp, List<String> warnings, String... properties) { for (String property : properties) { if (newAddOp.hasDefined(property) && newAddOp.get(property).getType() == EXPRESSION) { newAddOp.remove(property); warnings.add(ROOT_LOGGER.couldNotMigrateResourceAttributeWithExpression(property, pathAddress(newAddOp.get(OP_ADDR)))); } } } private void addLegacyEntries(ModelNode newAddOp) { newAddOp.get("legacy-entries").set(newAddOp.get(ENTRIES)); newAddOp.remove(ENTRIES); for (ModelNode legacyEntry : newAddOp.get("legacy-entries").asList()) { String newEntry = legacyEntry.asString() + NEW_ENTRY_SUFFIX; newAddOp.get(ENTRIES).add(newEntry); } } private void describeLegacyMessagingResources(OperationContext context, ModelNode legacyModelDescription) { ModelNode describeLegacySubsystem = createOperation(GenericSubsystemDescribeHandler.DEFINITION, context.getCurrentAddress()); context.addStep(legacyModelDescription, describeLegacySubsystem, GenericSubsystemDescribeHandler.INSTANCE, MODEL, true); } private void migrateConnectionFactory(ModelNode addOperation, String entrySuffix) { migrateConnectorAttribute(addOperation); migrateDiscoveryGroupNameAttribute(addOperation); if (!entrySuffix.isEmpty()) { List<ModelNode> entries = addOperation.get(ENTRIES).asList(); addOperation.remove(ENTRIES); for (ModelNode entry : entries) { String newEntry = entry.asString() + entrySuffix; addOperation.get(ENTRIES).add(newEntry); } } } private void migratePooledConnectionFactory(ModelNode addOperation) { migrateConnectorAttribute(addOperation); migrateDiscoveryGroupNameAttribute(addOperation); } private void migrateClusterConnection(ModelNode addOperation, List<String> warnings) { // connector-ref attribute has been renamed to connector-name addOperation.get("connector-name").set(addOperation.get(CONNECTOR_REF_STRING)); addOperation.remove(CONNECTOR_REF_STRING); ModelNode forwardWhenNoConsumers = addOperation.get(ClusterConnectionDefinition.FORWARD_WHEN_NO_CONSUMERS.getName()); if (forwardWhenNoConsumers.getType() == EXPRESSION) { warnings.add(ROOT_LOGGER.couldNotMigrateResourceAttributeWithExpression(ClusterConnectionDefinition.FORWARD_WHEN_NO_CONSUMERS.getName(), pathAddress(addOperation.get(OP_ADDR)))); } else { boolean value = forwardWhenNoConsumers.asBoolean(ClusterConnectionDefinition.FORWARD_WHEN_NO_CONSUMERS.getDefaultValue().asBoolean()); String messageLoadBalancingType = value ? "STRICT" : "ON_DEMAND"; addOperation.get("message-load-balancing-type").set(messageLoadBalancingType); } addOperation.remove(ClusterConnectionDefinition.FORWARD_WHEN_NO_CONSUMERS.getName()); migrateDiscoveryGroupNameAttribute(addOperation); } private void migrateConnectorAttribute(ModelNode addOperation) { ModelNode connector = addOperation.get(CONNECTOR); if (connector.isDefined()) { // legacy connector is a property list where the name is the connector and the value is undefined List<Property> connectorProps = connector.asPropertyList(); for (Property connectorProp : connectorProps) { addOperation.get("connectors").add(connectorProp.getName()); } addOperation.remove(CONNECTOR); } } private void migrateDiscoveryGroupNameAttribute(ModelNode addOperation) { ModelNode discoveryGroup = addOperation.get(DISCOVERY_GROUP_NAME); if (discoveryGroup.isDefined()) { // discovery-group-name attribute has been renamed to discovery-group addOperation.get("discovery-group").set(discoveryGroup); addOperation.remove(DISCOVERY_GROUP_NAME); } } private void migrateBridge(ModelNode addOperation) { migrateDiscoveryGroupNameAttribute(addOperation); } /** * For generic acceptor and connectors, migrate their factory-class attribute * if they are using the default Netty ones. */ private void migrateGenericTransport(ModelNode addOperation) { String factoryClass = addOperation.get(FACTORY_CLASS.getName()).asString(); final String newFactoryClass; switch (factoryClass) { case HORNETQ_NETTY_ACCEPTOR_FACTORY: newFactoryClass = ARTEMIS_NETTY_ACCEPTOR_FACTORY; break; case HORNETQ_NETTY_CONNECTOR_FACTORY: newFactoryClass = ARTEMIS_NETTY_CONNECTOR_FACTORY; break; default: newFactoryClass = factoryClass; } addOperation.get(FACTORY_CLASS.getName()).set(newFactoryClass); } private void migrateServer(PathAddress legacyAddress, ModelNode addOperation, Map<PathAddress, ModelNode> newAddOperations, List<String> warnings) { discardInterceptors(addOperation, CommonAttributes.REMOTING_INTERCEPTORS.getName(), warnings); discardInterceptors(addOperation, CommonAttributes.REMOTING_INCOMING_INTERCEPTORS.getName(), warnings); discardInterceptors(addOperation, CommonAttributes.REMOTING_OUTGOING_INTERCEPTORS.getName(), warnings); // add the server :add operation before eventually adding a ha-policy child :add operation in migrateHAPolicy. newAddOperations.put(pathAddress(addOperation.get(OP_ADDR)), addOperation); migrateHAPolicy(legacyAddress, addOperation, newAddOperations, warnings); } private void migrateHAPolicy(PathAddress legacyAddress, ModelNode serverAddOperation, Map<PathAddress, ModelNode> newAddOperations, List<String> warnings) { PathAddress serverAddress = PathAddress.pathAddress(serverAddOperation.get(OP_ADDR)); ModelNode sharedStoreAttr = serverAddOperation.get(SHARED_STORE.getName()); ModelNode backupAttr = serverAddOperation.get(BACKUP.getName()); if (sharedStoreAttr.getType() == EXPRESSION || backupAttr.getType() == EXPRESSION) { warnings.add(ROOT_LOGGER.couldNotMigrateHA(legacyAddress)); return; } boolean sharedStore = sharedStoreAttr.isDefined() ? sharedStoreAttr.asBoolean() : SHARED_STORE.getDefaultValue().asBoolean(); boolean backup = backupAttr.isDefined() ? backupAttr.asBoolean() : BACKUP.getDefaultValue().asBoolean(); ModelNode haPolicyAddOperation = createAddOperation(); final PathAddress haPolicyAddress; if (sharedStore) { if (backup) { haPolicyAddress = serverAddress.append(HA_POLICY, "shared-store-slave"); setAndDiscard(haPolicyAddOperation, serverAddOperation, ALLOW_FAILBACK, "allow-failback"); setAndDiscard(haPolicyAddOperation, serverAddOperation, FAILOVER_ON_SHUTDOWN, "failover-on-server-shutdown"); discardFailbackDelay(serverAddOperation, warnings); } else { haPolicyAddress = serverAddress.append(HA_POLICY, "shared-store-master"); setAndDiscard(haPolicyAddOperation, serverAddOperation, FAILOVER_ON_SHUTDOWN, "failover-on-server-shutdown"); discardFailbackDelay(serverAddOperation, warnings); } } else { if (backup) { haPolicyAddress = serverAddress.append(HA_POLICY, "replication-slave"); setAndDiscard(haPolicyAddOperation, serverAddOperation, ALLOW_FAILBACK, "allow-failback"); setAndDiscard(haPolicyAddOperation, serverAddOperation, MAX_SAVED_REPLICATED_JOURNAL_SIZE, "max-saved-replicated-journal-size"); setAndDiscard(haPolicyAddOperation, serverAddOperation, BACKUP_GROUP_NAME, "group-name"); discardFailbackDelay(serverAddOperation, warnings); } else { haPolicyAddress = serverAddress.append(HA_POLICY, "replication-master"); setAndDiscard(haPolicyAddOperation, serverAddOperation, CHECK_FOR_LIVE_SERVER, "check-for-live-server"); setAndDiscard(haPolicyAddOperation, serverAddOperation, BACKUP_GROUP_NAME, "group-name"); } } haPolicyAddOperation.get(OP_ADDR).set(haPolicyAddress.toModelNode()); newAddOperations.put(haPolicyAddress, haPolicyAddOperation); } private void discardInterceptors(ModelNode addOperation, String legacyInterceptorsAttributeName, List<String> warnings) { if (!addOperation.get(legacyInterceptorsAttributeName).isDefined()) { return; } warnings.add(ROOT_LOGGER.couldNotMigrateInterceptors(legacyInterceptorsAttributeName)); addOperation.remove(legacyInterceptorsAttributeName); } /** * Discard from a node and set it to a new node if the attribute is defined. Use the {@code newAttributeName} as the messaging-activemq may * named differently its corresponding attribute. */ private void setAndDiscard(ModelNode setNode, ModelNode discardNode, AttributeDefinition legacyAttributeDefinition, String newAttributeName) { ModelNode attribute = discardNode.get(legacyAttributeDefinition.getName()); if (attribute.isDefined()) { setNode.get(newAttributeName).set(attribute); discardNode.remove(legacyAttributeDefinition.getName()); } } private void discardUnsupportedAttribute(ModelNode newAddOp, AttributeDefinition legacyAttributeDefinition, List<String> warnings) { if (newAddOp.hasDefined(legacyAttributeDefinition.getName())) { newAddOp.remove(legacyAttributeDefinition.getName()); warnings.add(MessagingLogger.ROOT_LOGGER.couldNotMigrateUnsupportedAttribute(legacyAttributeDefinition.getName(), pathAddress(newAddOp.get(OP_ADDR)))); } } private void discardFailbackDelay(ModelNode newAddOp, List<String> warnings) { if (newAddOp.hasDefined(FAILBACK_DELAY.getName())) { newAddOp.remove(FAILBACK_DELAY.getName()); warnings.add(MessagingLogger.ROOT_LOGGER.couldNotMigrateFailbackDelayAttribute(pathAddress(newAddOp.get(OP_ADDR)))); } } }