/* * 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.postoffice.impl; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.config.WildcardConfiguration; import org.apache.activemq.artemis.core.postoffice.Address; import org.apache.activemq.artemis.core.postoffice.Binding; import org.apache.activemq.artemis.core.postoffice.Bindings; import org.apache.activemq.artemis.core.postoffice.BindingsFactory; import org.apache.activemq.artemis.core.transaction.Transaction; /** * extends the simple manager to allow wildcard addresses to be used. */ public class WildcardAddressManager extends SimpleAddressManager { /** * These are all the addresses, we use this so we can link back from the actual address to its linked wilcard addresses * or vice versa */ private final Map<SimpleString, Address> addresses = new ConcurrentHashMap<>(); private final Map<SimpleString, Address> wildCardAddresses = new ConcurrentHashMap<>(); public WildcardAddressManager(final BindingsFactory bindingsFactory, final WildcardConfiguration wildcardConfiguration) { super(bindingsFactory, wildcardConfiguration); } public WildcardAddressManager(final BindingsFactory bindingsFactory) { super(bindingsFactory); } @Override public Bindings getBindingsForRoutingAddress(final SimpleString address) throws Exception { Bindings bindings = super.getBindingsForRoutingAddress(address); // this should only happen if we're routing to an address that has no mappings when we're running checkAllowable if (bindings == null && !wildCardAddresses.isEmpty()) { Address add = addAndUpdateAddressMap(address); if (!add.containsWildCard()) { for (Address destAdd : add.getLinkedAddresses()) { Bindings b = super.getBindingsForRoutingAddress(destAdd.getAddress()); if (b != null) { Collection<Binding> theBindings = b.getBindings(); for (Binding theBinding : theBindings) { super.addMappingInternal(address, theBinding); } } } } bindings = super.getBindingsForRoutingAddress(address); } return bindings; } /** * If the address to add the binding to contains a wildcard then a copy of the binding (with the same underlying queue) * will be added to the actual mappings. Otherwise the binding is added as normal. * * @param binding the binding to add * @return true if the address was a new mapping */ @Override public boolean addBinding(final Binding binding) throws Exception { boolean exists = super.addBinding(binding); if (!exists) { Address add = addAndUpdateAddressMap(binding.getAddress()); if (add.containsWildCard()) { for (Address destAdd : add.getLinkedAddresses()) { super.addMappingInternal(destAdd.getAddress(), binding); } } else { for (Address destAdd : add.getLinkedAddresses()) { Bindings bindings = super.getBindingsForRoutingAddress(destAdd.getAddress()); for (Binding b : bindings.getBindings()) { super.addMappingInternal(binding.getAddress(), b); } } } } return exists; } /** * If the address is a wild card then the binding will be removed from the actual mappings for any linked address. * otherwise it will be removed as normal. * * @param uniqueName the name of the binding to remove * @return true if this was the last mapping for a specific address */ @Override public Binding removeBinding(final SimpleString uniqueName, Transaction tx) throws Exception { Binding binding = super.removeBinding(uniqueName, tx); if (binding != null) { Address add = getAddress(binding.getAddress()); if (add.containsWildCard()) { for (Address theAddress : add.getLinkedAddresses()) { super.removeBindingInternal(theAddress.getAddress(), uniqueName); } } removeAndUpdateAddressMap(add); } return binding; } @Override public void clear() { super.clear(); addresses.clear(); wildCardAddresses.clear(); } private Address getAddress(final SimpleString address) { Address add = new AddressImpl(address, wildcardConfiguration); Address actualAddress; if (add.containsWildCard()) { actualAddress = wildCardAddresses.get(address); } else { actualAddress = addresses.get(address); } return actualAddress != null ? actualAddress : add; } private synchronized Address addAndUpdateAddressMap(final SimpleString address) { Address add = new AddressImpl(address, wildcardConfiguration); Address actualAddress; if (add.containsWildCard()) { actualAddress = wildCardAddresses.get(address); } else { actualAddress = addresses.get(address); } if (actualAddress == null) { actualAddress = add; addAddress(address, actualAddress); } if (actualAddress.containsWildCard()) { for (Address destAdd : addresses.values()) { if (destAdd.matches(actualAddress)) { destAdd.addLinkedAddress(actualAddress); actualAddress.addLinkedAddress(destAdd); } } } else { for (Address destAdd : wildCardAddresses.values()) { if (actualAddress.matches(destAdd)) { destAdd.addLinkedAddress(actualAddress); actualAddress.addLinkedAddress(destAdd); } } } return actualAddress; } private void addAddress(final SimpleString address, final Address actualAddress) { if (actualAddress.containsWildCard()) { wildCardAddresses.put(address, actualAddress); } else { addresses.put(address, actualAddress); } } private synchronized void removeAndUpdateAddressMap(final Address address) throws Exception { // we only remove if there are no bindings left Bindings bindings = super.getBindingsForRoutingAddress(address.getAddress()); if (bindings == null || bindings.getBindings().size() == 0) { List<Address> addresses = address.getLinkedAddresses(); for (Address address1 : addresses) { address1.removeLinkedAddress(address); Bindings linkedBindings = super.getBindingsForRoutingAddress(address1.getAddress()); if (linkedBindings == null || linkedBindings.getBindings().size() == 0) { removeAddress(address1); } } removeAddress(address); } } private void removeAddress(final Address add) { if (add.containsWildCard()) { wildCardAddresses.remove(add.getAddress()); } else { addresses.remove(add.getAddress()); } } }