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