001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.plugin.java;
018
019import java.util.Arrays;
020import java.util.List;
021import java.util.Set;
022
023import org.apache.activemq.broker.Broker;
024import org.apache.activemq.broker.region.policy.PolicyEntry;
025import org.apache.activemq.broker.region.policy.PolicyMap;
026import org.apache.activemq.broker.region.virtual.VirtualDestination;
027import org.apache.activemq.command.ActiveMQDestination;
028import org.apache.activemq.network.DiscoveryNetworkConnector;
029import org.apache.activemq.plugin.AbstractRuntimeConfigurationBroker;
030import org.apache.activemq.plugin.UpdateVirtualDestinationsTask;
031import org.apache.activemq.plugin.util.PolicyEntryUtil;
032import org.apache.activemq.security.AuthorizationBroker;
033import org.apache.activemq.security.AuthorizationMap;
034import org.apache.activemq.security.SimpleAuthenticationBroker;
035import org.apache.activemq.security.SimpleAuthenticationPlugin;
036import org.slf4j.Logger;
037import org.slf4j.LoggerFactory;
038
039public class JavaRuntimeConfigurationBroker extends AbstractRuntimeConfigurationBroker {
040
041    /**
042     * @param next
043     */
044    public JavaRuntimeConfigurationBroker(Broker next) {
045        super(next);
046    }
047
048    public static final Logger LOG = LoggerFactory.getLogger(JavaRuntimeConfigurationBroker.class);
049
050
051    //Virtual Destinations
052    public void setVirtualDestinations(final VirtualDestination[] virtualDestinations) {
053        this.addDestinationWork.add(new UpdateVirtualDestinationsTask(this) {
054            @Override
055            protected VirtualDestination[] getVirtualDestinations() {
056                return virtualDestinations;
057            }
058        });
059    }
060
061    /**
062     * Set the virtual destinations and apply immediately, instead of waiting for a new
063     * destination or connection to trigger the work.
064     *
065     * @param virtualDestinations
066     * @param applyImmediately
067     * @throws Exception
068     */
069    public void setVirtualDestinations(final VirtualDestination[] virtualDestinations, boolean applyImmediately) throws Exception {
070        setVirtualDestinations(virtualDestinations);
071        if (applyImmediately) {
072            this.applyDestinationWork();
073        }
074    }
075
076    //New Destinations
077    public void setDestinations(final ActiveMQDestination[] destinations) {
078        for (ActiveMQDestination destination : destinations) {
079            try {
080                if (!containsDestination(destination)) {
081                    this.addDestination(this.getBrokerService().getAdminConnectionContext(), destination, true);
082                    this.info("Added destination " + destination);
083                }
084            } catch (Exception e) {
085                this.info("Failed to add a new destination for: " + destination, e);
086            }
087        }
088    }
089
090    protected boolean containsDestination(ActiveMQDestination destination) throws Exception {
091        return Arrays.asList(this.getBrokerService().getRegionBroker().getDestinations()).contains(destination);
092    }
093
094    public void addNewDestination(ActiveMQDestination destination) {
095        try {
096            this.addDestination(this.getBrokerService().getAdminConnectionContext(), destination, true);
097            this.info("Added destination " + destination);
098        } catch (Exception e) {
099            this.info("Failed to add a new destination for: " + destination, e);
100        }
101    }
102
103    //Network Connectors
104    public void addNetworkConnector(final DiscoveryNetworkConnector nc) {
105        try {
106            if (!getBrokerService().getNetworkConnectors().contains(nc)) {
107                getBrokerService().addNetworkConnector(nc);
108                nc.start();
109                info("started new network connector: " + nc);
110            } else {
111                info("skipping network connector add, already exists: " + nc);
112            }
113        } catch (Exception e) {
114            info("Failed to add new networkConnector " + nc, e);
115        }
116    }
117
118    public void updateNetworkConnector(final DiscoveryNetworkConnector nc) {
119        removeNetworkConnector(nc);
120        addNetworkConnector(nc);
121    }
122
123    public void removeNetworkConnector(final DiscoveryNetworkConnector existingCandidate) {
124        if (getBrokerService().removeNetworkConnector(existingCandidate)) {
125            try {
126                existingCandidate.stop();
127                info("stopped and removed networkConnector: " + existingCandidate);
128            } catch (Exception e) {
129                info("Failed to stop removed network connector: " + existingCandidate);
130            }
131        }
132    }
133
134    //Policy entries
135    public void addNewPolicyEntry(PolicyEntry addition) {
136        PolicyMap existingMap = getBrokerService().getDestinationPolicy();
137        existingMap.put(addition.getDestination(), addition);
138        PolicyEntryUtil.applyRetrospectively(this, addition, null);
139        info("added policy for: " + addition.getDestination());
140    }
141
142
143    /**
144     * This method will modify an existing policy entry that matches the destination
145     * set on the PolicyEntry passed in.
146     *
147     * The PolicyEntry reference must already be in the PolicyMap or it won't be updated.
148     * To modify the entry the best way is to look up the existing PolicyEntry from the
149     * PolicyMap, make changes to it, and pass it to this method to apply.
150     *
151     * To create or replace an existing entry (if the destination matches), see
152     * {@link #modifyPolicyEntry(PolicyEntry, boolean)
153     *
154     *
155     * @param existing
156     */
157    public void modifyPolicyEntry(PolicyEntry existing) {
158        modifyPolicyEntry(existing, false);
159    }
160
161    public void modifyPolicyEntry(PolicyEntry existing, boolean createOrReplace) {
162        modifyPolicyEntry(existing, createOrReplace, null);
163    }
164
165    /**
166     * This method will modify an existing policy entry that matches the destination
167     * set on the PolicyEntry passed in.  If createOrReplace is true, a new policy
168     * will be created if it doesn't exist and a policy will be replaced in the PolicyMap,
169     * versus modified, if it is a different reference but the destinations for the Policy match.
170     *
171     * If createOrReplace is false, the policy update will only be applied if
172     * the PolicyEntry reference already exists in the PolicyMap.
173     *
174     * includedProperties is a list of properties that will be applied retrospectively. If
175     * the list is null, then all properties on the policy will be reapplied to the destination.
176     * This allows the ability to limit which properties are applied to existing destinations.
177     *
178     * @param existing
179     * @param createIfAbsent
180     * @param includedProperties - optional list of properties to apply retrospectively
181     */
182    public void modifyPolicyEntry(PolicyEntry existing, boolean createOrReplace,
183            Set<String> includedProperties) {
184        PolicyMap existingMap = this.getBrokerService().getDestinationPolicy();
185
186        //First just look up by the destination type to see if anything matches
187        PolicyEntry existingEntry = PolicyEntryUtil.findEntryByDestination(this, existing);
188
189        //handle createOrReplace
190        if (createOrReplace) {
191            //if not found at all, go ahead and insert the policy entry
192            if (existingEntry == null) {
193                existingMap.put(existing.getDestination(), existing);
194                existingEntry = existing;
195            //If found but the objects are different, remove the old policy entry
196            //and replace it with the new one
197            } else if (!existing.equals(existingEntry)) {
198                synchronized(existingMap) {
199                    existingMap.remove(existingEntry.getDestination(), existingEntry);
200                    existingMap.put(existing.getDestination(), existing);
201                }
202                existingEntry = existing;
203            }
204        }
205
206        //Make sure that at this point the passed in object and the entry in
207        //the map are the same
208        if (existingEntry != null && existingEntry.equals(existing)) {
209            PolicyEntryUtil.applyRetrospectively(this, existingEntry, includedProperties);
210            this.info("updated policy for: " + existingEntry.getDestination());
211        } else {
212            throw new IllegalArgumentException("The policy can not be updated because it either does not exist or the PolicyEntry"
213                    + " reference does not match an existing PolicyEntry in the PolicyMap.  To replace an"
214                    + " entry (versus modifying) or add, set createOrReplace to true. "
215                    + existing + ", destination:" + existing.getDestination());
216        }
217    }
218
219    //authentication plugin
220    public void updateSimpleAuthenticationPlugin(final SimpleAuthenticationPlugin updatedPlugin) {
221        try {
222            final SimpleAuthenticationBroker authenticationBroker =
223                (SimpleAuthenticationBroker) getBrokerService().getBroker().getAdaptor(SimpleAuthenticationBroker.class);
224            addConnectionWork.add(new Runnable() {
225                @Override
226                public void run() {
227                    authenticationBroker.setUserGroups(updatedPlugin.getUserGroups());
228                    authenticationBroker.setUserPasswords(updatedPlugin.getUserPasswords());
229                    authenticationBroker.setAnonymousAccessAllowed(updatedPlugin.isAnonymousAccessAllowed());
230                    authenticationBroker.setAnonymousUser(updatedPlugin.getAnonymousUser());
231                    authenticationBroker.setAnonymousGroup(updatedPlugin.getAnonymousGroup());
232                }
233            });
234        } catch (Exception e) {
235            info("failed to apply SimpleAuthenticationPlugin modifications to SimpleAuthenticationBroker", e);
236        }
237    }
238
239    //authorization map
240    public void updateAuthorizationMap(final AuthorizationMap authorizationMap) {
241        try {
242            // replace authorization map - need exclusive write lock to total broker
243            AuthorizationBroker authorizationBroker =
244                    (AuthorizationBroker) getBrokerService().getBroker().getAdaptor(AuthorizationBroker.class);
245
246            authorizationBroker.setAuthorizationMap(authorizationMap);
247        } catch (Exception e) {
248            info("failed to apply modified AuthorizationMap to AuthorizationBroker", e);
249        }
250    }
251}