All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.activemq.artemis.core.server.group.impl.RemoteGroupingHandler Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.activemq.artemis.core.server.group.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.management.CoreNotificationType;
import org.apache.activemq.artemis.api.core.management.ManagementHelper;
import org.apache.activemq.artemis.core.postoffice.BindingType;
import org.apache.activemq.artemis.core.server.ActiveMQMessageBundle;
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
import org.apache.activemq.artemis.core.server.management.ManagementService;
import org.apache.activemq.artemis.core.server.management.Notification;
import org.apache.activemq.artemis.utils.ExecutorFactory;
import org.apache.activemq.artemis.utils.collections.ConcurrentHashSet;
import org.apache.activemq.artemis.utils.collections.TypedProperties;

/**
 * A remote Grouping handler.
 * 

* This will use management notifications to communicate with the node that has the Local Grouping * handler to make proposals. */ public final class RemoteGroupingHandler extends GroupHandlingAbstract { private final SimpleString name; private final Map responses = new ConcurrentHashMap<>(); private final Lock lock = new ReentrantLock(); private final Condition sendCondition = lock.newCondition(); private final long timeout; private final long groupTimeout; private final ConcurrentMap> groupMap = new ConcurrentHashMap<>(); private final ConcurrentHashSet pendingNotifications = new ConcurrentHashSet<>(); private boolean started = false; public RemoteGroupingHandler(final ExecutorFactory executorFactory, final ManagementService managementService, final SimpleString name, final SimpleString address, final long timeout, final long groupTimeout) { super(executorFactory != null ? executorFactory.getExecutor() : null, managementService, address); this.name = name; this.timeout = timeout; this.groupTimeout = groupTimeout; } public RemoteGroupingHandler(final ManagementService managementService, final SimpleString name, final SimpleString address, final long timeout, final long groupTimeout) { this(null, managementService, name, address, timeout, groupTimeout); } @Override public SimpleString getName() { return name; } @Override public void start() throws Exception { if (started) return; started = true; } @Override public void stop() throws Exception { started = false; } @Override public boolean isStarted() { return started; } @Override public void resendPending() throws Exception { // In case the RESET wasn't sent yet to the remote node, we may eventually miss a node send, // on that case the cluster-reset information will ask the group to resend any pending information try { lock.lock(); for (Notification notification : pendingNotifications) { managementService.sendNotification(notification); } } finally { lock.unlock(); } } @Override public Response propose(final Proposal proposal) throws Exception { // return it from the cache first Response response = responses.get(proposal.getGroupId()); if (response != null) { checkTimeout(response); return response; } if (!started) { throw ActiveMQMessageBundle.BUNDLE.groupWhileStopping(); } Notification notification = null; try { lock.lock(); notification = createProposalNotification(proposal.getGroupId(), proposal.getClusterName()); pendingNotifications.add(notification); managementService.sendNotification(notification); long timeLimit = System.currentTimeMillis() + timeout; do { sendCondition.await(timeout, TimeUnit.MILLISECONDS); response = responses.get(proposal.getGroupId()); // You could have this response being null if you had multiple threads calling propose if (response != null) { break; } } while (timeLimit > System.currentTimeMillis()); } finally { if (notification != null) { pendingNotifications.remove(notification); } lock.unlock(); } if (response == null) { ActiveMQServerLogger.LOGGER.groupHandlerSendTimeout(); } return response; } @Override public void awaitBindings() { // NO-OP } private void checkTimeout(Response response) { if (response != null) { if (groupTimeout > 0 && ((response.getTimeUsed() + groupTimeout) < System.currentTimeMillis())) { // We just touch the group on the local server at the half of the timeout // to avoid the group from expiring response.use(); try { managementService.sendNotification(createProposalNotification(response.getGroupId(), response.getClusterName())); } catch (Exception ignored) { } } } } private Notification createProposalNotification(SimpleString groupId, SimpleString clusterName) { TypedProperties props = new TypedProperties(); props.putSimpleStringProperty(ManagementHelper.HDR_PROPOSAL_GROUP_ID, groupId); props.putSimpleStringProperty(ManagementHelper.HDR_PROPOSAL_VALUE, clusterName); props.putIntProperty(ManagementHelper.HDR_BINDING_TYPE, BindingType.LOCAL_QUEUE_INDEX); props.putSimpleStringProperty(ManagementHelper.HDR_ADDRESS, address); props.putIntProperty(ManagementHelper.HDR_DISTANCE, 0); return new Notification(null, CoreNotificationType.PROPOSAL, props); } @Override public Response getProposal(final SimpleString fullID, boolean touchTime) { Response response = responses.get(fullID); if (touchTime) { checkTimeout(response); } return response; } @Override public void remove(SimpleString groupid, SimpleString clusterName) throws Exception { List groups = groupMap.get(clusterName); if (groups != null) { groups.remove(groupid); } responses.remove(groupid); fireUnproposed(groupid); } @Override public void remove(SimpleString groupid, SimpleString clusterName, int distance) throws Exception { remove(groupid, clusterName); sendUnproposal(groupid, clusterName, distance); } @Override public void proposed(final Response response) throws Exception { try { lock.lock(); responses.put(response.getGroupId(), response); List newList = new ArrayList<>(); List oldList = groupMap.putIfAbsent(response.getChosenClusterName(), newList); if (oldList != null) { newList = oldList; } newList.add(response.getGroupId()); // We could have more than one Requests waiting in case you have multiple producers // using different groups sendCondition.signalAll(); } finally { lock.unlock(); } } @Override public Response receive(final Proposal proposal, final int distance) throws Exception { TypedProperties props = new TypedProperties(); props.putSimpleStringProperty(ManagementHelper.HDR_PROPOSAL_GROUP_ID, proposal.getGroupId()); props.putSimpleStringProperty(ManagementHelper.HDR_PROPOSAL_VALUE, proposal.getClusterName()); props.putIntProperty(ManagementHelper.HDR_BINDING_TYPE, BindingType.LOCAL_QUEUE_INDEX); props.putSimpleStringProperty(ManagementHelper.HDR_ADDRESS, address); props.putIntProperty(ManagementHelper.HDR_DISTANCE, distance); Notification notification = new Notification(null, CoreNotificationType.PROPOSAL, props); managementService.sendNotification(notification); return null; } @Override public void sendProposalResponse(final Response response, final int distance) throws Exception { // NO-OP } @Override public void addGroupBinding(final GroupBinding groupBinding) { // NO-OP } @Override public void onNotification(final Notification notification) { if (!(notification.getType() instanceof CoreNotificationType)) return; // removing the groupid if the binding has been removed if (notification.getType() == CoreNotificationType.BINDING_REMOVED) { SimpleString clusterName = notification.getProperties().getSimpleStringProperty(ManagementHelper.HDR_CLUSTER_NAME); List list = groupMap.remove(clusterName); if (list != null) { for (SimpleString val : list) { if (val != null) { responses.remove(val); } } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy