blob: cdf44822ef341183b4a8e2f4d7adfdce853039e9 [file] [log] [blame]
/*
*
* 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.qpid.server.configuration.store;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.apache.log4j.Logger;
import org.apache.qpid.server.BrokerOptions;
import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.model.Broker;
import org.apache.qpid.server.model.ConfiguredObject;
import org.apache.qpid.server.model.Port;
import org.apache.qpid.server.model.Protocol;
import org.apache.qpid.server.model.State;
import org.apache.qpid.server.model.VirtualHost;
import org.apache.qpid.server.store.ConfiguredObjectRecord;
import org.apache.qpid.server.store.ConfiguredObjectRecordImpl;
import org.apache.qpid.server.store.DurableConfigurationStore;
import org.apache.qpid.server.store.StoreException;
import org.apache.qpid.server.store.handler.ConfiguredObjectRecordHandler;
import org.apache.qpid.server.util.MapValueConverter;
public class ManagementModeStoreHandler implements DurableConfigurationStore
{
private static final Logger LOGGER = Logger.getLogger(ManagementModeStoreHandler.class);
private static final String MANAGEMENT_MODE_PORT_PREFIX = "MANAGEMENT-MODE-PORT-";
private static final String PORT_TYPE = Port.class.getSimpleName();
private static final String VIRTUAL_HOST_TYPE = VirtualHost.class.getSimpleName();
private static final String ATTRIBUTE_STATE = VirtualHost.STATE;
private static final Object MANAGEMENT_MODE_AUTH_PROVIDER = "mm-auth";
private final DurableConfigurationStore _store;
private Map<UUID, ConfiguredObjectRecord> _cliEntries;
private final Map<UUID, Object> _quiescedEntriesOriginalState;
private final BrokerOptions _options;
private ConfiguredObject<?> _parent;
private HashMap<UUID, ConfiguredObjectRecord> _records;
public ManagementModeStoreHandler(DurableConfigurationStore store,
BrokerOptions options)
{
_options = options;
_store = store;
_quiescedEntriesOriginalState = quiesceEntries(options);
}
@Override
public void openConfigurationStore(final ConfiguredObject<?> parent, final Map<String, Object> storeSettings)
throws StoreException
{
_parent = parent;
_store.openConfigurationStore(parent,storeSettings);
_records = new HashMap<UUID, ConfiguredObjectRecord>();
final ConfiguredObjectRecordHandler localRecoveryHandler = new ConfiguredObjectRecordHandler()
{
private int _version;
private boolean _quiesceRmiPort = _options.getManagementModeRmiPortOverride() > 0;
private boolean _quiesceJmxPort = _options.getManagementModeJmxPortOverride() > 0;
private boolean _quiesceHttpPort = _options.getManagementModeHttpPortOverride() > 0;
@Override
public void begin(final int configVersion)
{
_version = configVersion;
}
@Override
public boolean handle(final ConfiguredObjectRecord object)
{
String entryType = object.getType();
Map<String, Object> attributes = object.getAttributes();
boolean quiesce = false;
if (VIRTUAL_HOST_TYPE.equals(entryType) && _options.isManagementModeQuiesceVirtualHosts())
{
quiesce = true;
}
else if (PORT_TYPE.equals(entryType))
{
if (attributes == null)
{
throw new IllegalConfigurationException("Port attributes are not set in " + object);
}
Set<Protocol> protocols = getPortProtocolsAttribute(attributes);
if (protocols == null)
{
quiesce = true;
}
else
{
for (Protocol protocol : protocols)
{
switch (protocol)
{
case JMX_RMI:
quiesce = _quiesceJmxPort || _quiesceRmiPort ;
break;
case RMI:
quiesce = _quiesceRmiPort;
break;
case HTTP:
quiesce = _quiesceHttpPort;
break;
default:
quiesce = true;
}
}
}
}
if (quiesce)
{
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Management mode quiescing entry " + object);
}
// save original state
_quiescedEntriesOriginalState.put(object.getId(), attributes.get(ATTRIBUTE_STATE));
Map<String,Object> modifiedAttributes = new HashMap<String, Object>(attributes);
modifiedAttributes.put(ATTRIBUTE_STATE, State.QUIESCED);
ConfiguredObjectRecord record = new ConfiguredObjectRecordImpl(object.getId(), object.getType(), modifiedAttributes, object.getParents());
_records.put(record.getId(), record);
}
else
{
_records.put(object.getId(), object);
}
return true;
}
@Override
public int end()
{
return _version;
}
};
_store.visitConfiguredObjectRecords(localRecoveryHandler);
_cliEntries = createPortsFromCommandLineOptions(_options);
for(ConfiguredObjectRecord entry : _cliEntries.values())
{
_records.put(entry.getId(),entry);
}
}
@Override
public void visitConfiguredObjectRecords(final ConfiguredObjectRecordHandler recoveryHandler) throws StoreException
{
recoveryHandler.begin(0);
for(ConfiguredObjectRecord record : _records.values())
{
if(!recoveryHandler.handle(record))
{
break;
}
}
recoveryHandler.end();
}
@Override
public void create(final ConfiguredObjectRecord object)
{
synchronized (_store)
{
_store.create(object);
}
_records.put(object.getId(), object);
}
@Override
public void update(final boolean createIfNecessary, final ConfiguredObjectRecord... records) throws StoreException
{
synchronized (_store)
{
Collection<ConfiguredObjectRecord> actualUpdates = new ArrayList<ConfiguredObjectRecord>();
for(ConfiguredObjectRecord record : records)
{
if (_cliEntries.containsKey(record.getId()))
{
throw new IllegalConfigurationException("Cannot save configuration provided as command line argument:"
+ record);
}
else if (_quiescedEntriesOriginalState.containsKey(record.getId()))
{
// save entry with the original state
record = createEntryWithState(record, _quiescedEntriesOriginalState.get(record.getId()));
}
actualUpdates.add(record);
}
_store.update(createIfNecessary, actualUpdates.toArray(new ConfiguredObjectRecord[actualUpdates.size()]));
}
for(ConfiguredObjectRecord record : records)
{
_records.put(record.getId(), record);
}
}
@Override
public void closeConfigurationStore() throws StoreException
{
}
@Override
public synchronized UUID[] remove(final ConfiguredObjectRecord... records)
{
synchronized (_store)
{
UUID[] idsToRemove = new UUID[records.length];
for(int i = 0; i < records.length; i++)
{
idsToRemove[i] = records[i].getId();
}
for (UUID id : idsToRemove)
{
if (_cliEntries.containsKey(id))
{
throw new IllegalConfigurationException("Cannot change configuration for command line entry:"
+ _cliEntries.get(id));
}
}
UUID[] result = _store.remove(records);
for (UUID id : idsToRemove)
{
if (_quiescedEntriesOriginalState.containsKey(id))
{
_quiescedEntriesOriginalState.remove(id);
}
}
for(ConfiguredObjectRecord record : records)
{
_records.remove(record.getId());
}
return result;
}
}
private Map<UUID, ConfiguredObjectRecord> createPortsFromCommandLineOptions(BrokerOptions options)
{
int managementModeRmiPortOverride = options.getManagementModeRmiPortOverride();
if (managementModeRmiPortOverride < 0)
{
throw new IllegalConfigurationException("Invalid rmi port is specified: " + managementModeRmiPortOverride);
}
int managementModeJmxPortOverride = options.getManagementModeJmxPortOverride();
if (managementModeJmxPortOverride < 0)
{
throw new IllegalConfigurationException("Invalid jmx port is specified: " + managementModeJmxPortOverride);
}
int managementModeHttpPortOverride = options.getManagementModeHttpPortOverride();
if (managementModeHttpPortOverride < 0)
{
throw new IllegalConfigurationException("Invalid http port is specified: " + managementModeHttpPortOverride);
}
Map<UUID, ConfiguredObjectRecord> cliEntries = new HashMap<UUID, ConfiguredObjectRecord>();
if (managementModeRmiPortOverride != 0)
{
ConfiguredObjectRecord entry = createCLIPortEntry(managementModeRmiPortOverride, Protocol.RMI);
cliEntries.put(entry.getId(), entry);
if (managementModeJmxPortOverride == 0)
{
ConfiguredObjectRecord connectorEntry = createCLIPortEntry(managementModeRmiPortOverride + 100, Protocol.JMX_RMI);
cliEntries.put(connectorEntry.getId(), connectorEntry);
}
}
if (managementModeJmxPortOverride != 0)
{
ConfiguredObjectRecord entry = createCLIPortEntry(managementModeJmxPortOverride, Protocol.JMX_RMI);
cliEntries.put(entry.getId(), entry);
}
if (managementModeHttpPortOverride != 0)
{
ConfiguredObjectRecord entry = createCLIPortEntry(managementModeHttpPortOverride, Protocol.HTTP);
cliEntries.put(entry.getId(), entry);
}
return cliEntries;
}
private ConfiguredObjectRecord createCLIPortEntry(int port, Protocol protocol)
{
ConfiguredObjectRecord parent = findBroker();
Map<String, Object> attributes = new HashMap<String, Object>();
attributes.put(Port.PORT, port);
attributes.put(Port.PROTOCOLS, Collections.singleton(protocol));
attributes.put(Port.NAME, MANAGEMENT_MODE_PORT_PREFIX + protocol.name());
if (protocol != Protocol.RMI)
{
attributes.put(Port.AUTHENTICATION_PROVIDER, MANAGEMENT_MODE_AUTH_PROVIDER);
}
ConfiguredObjectRecord portEntry = new ConfiguredObjectRecordImpl(UUID.randomUUID(), PORT_TYPE, attributes,
Collections.singletonMap(parent.getType(),parent));
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Add management mode port configuration " + portEntry + " for port " + port + " and protocol "
+ protocol);
}
return portEntry;
}
private ConfiguredObjectRecord findBroker()
{
for(ConfiguredObjectRecord record : _records.values())
{
if(record.getType().equals(Broker.class.getSimpleName()))
{
return record;
}
}
return null;
}
private Map<UUID, Object> quiesceEntries(final BrokerOptions options)
{
final Map<UUID, Object> quiescedEntries = new HashMap<UUID, Object>();
final int managementModeRmiPortOverride = options.getManagementModeRmiPortOverride();
final int managementModeJmxPortOverride = options.getManagementModeJmxPortOverride();
final int managementModeHttpPortOverride = options.getManagementModeHttpPortOverride();
_store.visitConfiguredObjectRecords(new ConfiguredObjectRecordHandler()
{
@Override
public void begin(final int configVersion)
{
}
@Override
public boolean handle(final ConfiguredObjectRecord entry)
{
String entryType = entry.getType();
Map<String, Object> attributes = entry.getAttributes();
boolean quiesce = false;
if (VIRTUAL_HOST_TYPE.equals(entryType) && options.isManagementModeQuiesceVirtualHosts())
{
quiesce = true;
}
else if (PORT_TYPE.equals(entryType))
{
if (attributes == null)
{
throw new IllegalConfigurationException("Port attributes are not set in " + entry);
}
Set<Protocol> protocols = getPortProtocolsAttribute(attributes);
if (protocols == null)
{
quiesce = true;
}
else
{
for (Protocol protocol : protocols)
{
switch (protocol)
{
case JMX_RMI:
quiesce = managementModeJmxPortOverride > 0 || managementModeRmiPortOverride > 0;
break;
case RMI:
quiesce = managementModeRmiPortOverride > 0;
break;
case HTTP:
quiesce = managementModeHttpPortOverride > 0;
break;
default:
quiesce = true;
}
}
}
}
if (quiesce)
{
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Management mode quiescing entry " + entry);
}
// save original state
quiescedEntries.put(entry.getId(), attributes.get(ATTRIBUTE_STATE));
}
return true;
}
@Override
public int end()
{
return 0;
}
});
return quiescedEntries;
}
private Set<Protocol> getPortProtocolsAttribute(Map<String, Object> attributes)
{
Object object = attributes.get(Port.PROTOCOLS);
if (object == null)
{
return null;
}
return MapValueConverter.getEnumSetAttribute(Port.PROTOCOLS, attributes, Protocol.class);
}
private ConfiguredObjectRecord createEntryWithState(ConfiguredObjectRecord entry, Object state)
{
Map<String, Object> attributes = new HashMap<String, Object>(entry.getAttributes());
if (state == null)
{
attributes.remove(ATTRIBUTE_STATE);
}
else
{
attributes.put(ATTRIBUTE_STATE, state);
}
return new ConfiguredObjectRecordImpl(entry.getId(), entry.getType(), attributes, entry.getParents());
}
}