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