content: /**
   * 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.plugin.java;
  
  import java.util.Arrays;
  import java.util.Set;
  
  import org.apache.activemq.broker.Broker;
  import org.apache.activemq.broker.region.policy.PolicyEntry;
  import org.apache.activemq.broker.region.policy.PolicyMap;
  import org.apache.activemq.broker.region.virtual.VirtualDestination;
  import org.apache.activemq.command.ActiveMQDestination;
  import org.apache.activemq.network.DiscoveryNetworkConnector;
  import org.apache.activemq.plugin.AbstractRuntimeConfigurationBroker;
  import org.apache.activemq.plugin.UpdateVirtualDestinationsTask;
  import org.apache.activemq.plugin.util.PolicyEntryUtil;
  import org.apache.activemq.security.AuthorizationBroker;
  import org.apache.activemq.security.AuthorizationMap;
  import org.apache.activemq.security.SimpleAuthenticationBroker;
  import org.apache.activemq.security.SimpleAuthenticationPlugin;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  public class JavaRuntimeConfigurationBroker extends AbstractRuntimeConfigurationBroker {
  
      /**
       * @param next
       */
      public JavaRuntimeConfigurationBroker(Broker next) {
          super(next);
      }
  
      public static final Logger LOG = LoggerFactory.getLogger(JavaRuntimeConfigurationBroker.class);
  
  
      //Virtual Destinations
      public void setVirtualDestinations(final VirtualDestination[] virtualDestinations) {
          this.addDestinationWork.add(new UpdateVirtualDestinationsTask(this) {
              @Override
              protected VirtualDestination[] getVirtualDestinations() {
                  return virtualDestinations;
              }
          });
      }
  
      /**
       * Set the virtual destinations and apply immediately, instead of waiting for a new
       * destination or connection to trigger the work.
       *
       * @param virtualDestinations
       * @param applyImmediately
       * @throws Exception
       */
      public void setVirtualDestinations(final VirtualDestination[] virtualDestinations, boolean applyImmediately) throws Exception {
          setVirtualDestinations(virtualDestinations);
          if (applyImmediately) {
              this.applyDestinationWork();
          }
      }
  
      //New Destinations
      public void setDestinations(final ActiveMQDestination[] destinations) {
          for (ActiveMQDestination destination : destinations) {
              try {
                  if (!containsDestination(destination)) {
                      this.addDestination(this.getBrokerService().getAdminConnectionContext(), destination, true);
                      this.info("Added destination " + destination);
                  }
              } catch (Exception e) {
                  this.info("Failed to add a new destination for: " + destination, e);
              }
          }
      }
  
      protected boolean containsDestination(ActiveMQDestination destination) throws Exception {
          return Arrays.asList(this.getBrokerService().getRegionBroker().getDestinations()).contains(destination);
      }
  
      public void addNewDestination(ActiveMQDestination destination) {
          try {
              this.addDestination(this.getBrokerService().getAdminConnectionContext(), destination, true);
              this.info("Added destination " + destination);
          } catch (Exception e) {
              this.info("Failed to add a new destination for: " + destination, e);
          }
      }
  
      //Network Connectors
      public void addNetworkConnector(final DiscoveryNetworkConnector nc) {
          try {
              if (!getBrokerService().getNetworkConnectors().contains(nc)) {
                  getBrokerService().addNetworkConnector(nc);
                  getBrokerService().startNetworkConnector(nc, null);
                  info("started new network connector: " + nc);
              } else {
                  info("skipping network connector add, already exists: " + nc);
              }
          } catch (Exception e) {
              info("Failed to add new networkConnector " + nc, e);
          }
      }
  
      public void updateNetworkConnector(final DiscoveryNetworkConnector nc) {
          removeNetworkConnector(nc);
          addNetworkConnector(nc);
      }
  
      public void removeNetworkConnector(final DiscoveryNetworkConnector existingCandidate) {
          if (getBrokerService().removeNetworkConnector(existingCandidate)) {
              try {
                  existingCandidate.stop();
                  info("stopped and removed networkConnector: " + existingCandidate);
              } catch (Exception e) {
                  info("Failed to stop removed network connector: " + existingCandidate);
              }
          }
      }
  
      //Policy entries
      public void addNewPolicyEntry(PolicyEntry addition) {
          PolicyMap existingMap = getBrokerService().getDestinationPolicy();
          existingMap.put(addition.getDestination(), addition);
          PolicyEntryUtil.applyRetrospectively(this, addition, null);
          info("added policy for: " + addition.getDestination());
      }
  
  
      /**
       * This method will modify an existing policy entry that matches the destination
       * set on the PolicyEntry passed in.
       *
       * The PolicyEntry reference must already be in the PolicyMap or it won't be updated.
       * To modify the entry the best way is to look up the existing PolicyEntry from the
       * PolicyMap, make changes to it, and pass it to this method to apply.
       *
       * To create or replace an existing entry (if the destination matches), see
       * {@link #modifyPolicyEntry(PolicyEntry, boolean)
       *
       *
       * @param existing
       */
      public void modifyPolicyEntry(PolicyEntry existing) {
          modifyPolicyEntry(existing, false);
      }
  
      public void modifyPolicyEntry(PolicyEntry existing, boolean createOrReplace) {
          modifyPolicyEntry(existing, createOrReplace, null);
      }
  
      /**
       * This method will modify an existing policy entry that matches the destination
       * set on the PolicyEntry passed in.  If createOrReplace is true, a new policy
       * will be created if it doesn't exist and a policy will be replaced in the PolicyMap,
       * versus modified, if it is a different reference but the destinations for the Policy match.
       *
       * If createOrReplace is false, the policy update will only be applied if
       * the PolicyEntry reference already exists in the PolicyMap.
       *
       * includedProperties is a list of properties that will be applied retrospectively. If
       * the list is null, then all properties on the policy will be reapplied to the destination.
       * This allows the ability to limit which properties are applied to existing destinations.
       *
       * @param existing
       * @param createOrReplace
       * @param includedProperties - optional list of properties to apply retrospectively
       */
      public void modifyPolicyEntry(PolicyEntry existing, boolean createOrReplace,
              Set<String> includedProperties) {
          PolicyMap existingMap = this.getBrokerService().getDestinationPolicy();
  
          //First just look up by the destination type to see if anything matches
          PolicyEntry existingEntry = PolicyEntryUtil.findEntryByDestination(this, existing);
  
          //handle createOrReplace
          if (createOrReplace) {
              //if not found at all, go ahead and insert the policy entry
              if (existingEntry == null) {
                  existingMap.put(existing.getDestination(), existing);
                  existingEntry = existing;
              //If found but the objects are different, remove the old policy entry
              //and replace it with the new one
              } else if (!existing.equals(existingEntry)) {
                  synchronized(existingMap) {
                      existingMap.remove(existingEntry.getDestination(), existingEntry);
                      existingMap.put(existing.getDestination(), existing);
                  }
                  existingEntry = existing;
              }
          }
  
          //Make sure that at this point the passed in object and the entry in
          //the map are the same
          if (existingEntry != null && existingEntry.equals(existing)) {
              PolicyEntryUtil.applyRetrospectively(this, existingEntry, includedProperties);
              this.info("updated policy for: " + existingEntry.getDestination());
          } else {
              throw new IllegalArgumentException("The policy can not be updated because it either does not exist or the PolicyEntry"
                      + " reference does not match an existing PolicyEntry in the PolicyMap.  To replace an"
                      + " entry (versus modifying) or add, set createOrReplace to true. "
                      + existing + ", destination:" + existing.getDestination());
          }
      }
  
      //authentication plugin
      public void updateSimpleAuthenticationPlugin(final SimpleAuthenticationPlugin updatedPlugin) {
          try {
              final SimpleAuthenticationBroker authenticationBroker =
                  (SimpleAuthenticationBroker) getBrokerService().getBroker().getAdaptor(SimpleAuthenticationBroker.class);
              addConnectionWork.add(new Runnable() {
                  @Override
                  public void run() {
                      authenticationBroker.setUserGroups(updatedPlugin.getUserGroups());
                      authenticationBroker.setUserPasswords(updatedPlugin.getUserPasswords());
                      authenticationBroker.setAnonymousAccessAllowed(updatedPlugin.isAnonymousAccessAllowed());
                      authenticationBroker.setAnonymousUser(updatedPlugin.getAnonymousUser());
                      authenticationBroker.setAnonymousGroup(updatedPlugin.getAnonymousGroup());
                  }
              });
          } catch (Exception e) {
              info("failed to apply SimpleAuthenticationPlugin modifications to SimpleAuthenticationBroker", e);
          }
      }
  
      //authorization map
      public void updateAuthorizationMap(final AuthorizationMap authorizationMap) {
          try {
              // replace authorization map - need exclusive write lock to total broker
              AuthorizationBroker authorizationBroker =
                      (AuthorizationBroker) getBrokerService().getBroker().getAdaptor(AuthorizationBroker.class);
  
              authorizationBroker.setAuthorizationMap(authorizationMap);
          } catch (Exception e) {
              info("failed to apply modified AuthorizationMap to AuthorizationBroker", e);
          }
      }
  }