/*
*
* 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.model.adapter;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.apache.qpid.server.configuration.BrokerProperties;
import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.model.Broker;
import org.apache.qpid.server.model.Port;
import org.apache.qpid.server.model.Protocol;
import org.apache.qpid.server.model.Protocol.ProtocolType;
import org.apache.qpid.server.model.Transport;
import org.apache.qpid.server.util.MapValueConverter;
public class PortFactory
{
public static final int DEFAULT_AMQP_SEND_BUFFER_SIZE = 262144;
public static final int DEFAULT_AMQP_RECEIVE_BUFFER_SIZE = 262144;
public static final boolean DEFAULT_AMQP_NEED_CLIENT_AUTH = false;
public static final boolean DEFAULT_AMQP_WANT_CLIENT_AUTH = false;
public static final boolean DEFAULT_AMQP_TCP_NO_DELAY = true;
public static final String DEFAULT_AMQP_BINDING = "*";
public static final Transport DEFAULT_TRANSPORT = Transport.TCP;
private final Collection<Protocol> _defaultProtocols;
public PortFactory()
{
Set<Protocol> defaultProtocols = EnumSet.of(Protocol.AMQP_0_8, Protocol.AMQP_0_9, Protocol.AMQP_0_9_1,
Protocol.AMQP_0_10, Protocol.AMQP_1_0);
String excludedProtocols = System.getProperty(BrokerProperties.PROPERTY_BROKER_DEFAULT_AMQP_PROTOCOL_EXCLUDES);
if (excludedProtocols != null)
{
String[] excludes = excludedProtocols.split(",");
for (String exclude : excludes)
{
Protocol protocol = Protocol.valueOf(exclude);
defaultProtocols.remove(protocol);
}
}
String includedProtocols = System.getProperty(BrokerProperties.PROPERTY_BROKER_DEFAULT_AMQP_PROTOCOL_INCLUDES);
if (includedProtocols != null)
{
String[] includes = includedProtocols.split(",");
for (String include : includes)
{
Protocol protocol = Protocol.valueOf(include);
defaultProtocols.add(protocol);
}
}
_defaultProtocols = Collections.unmodifiableCollection(defaultProtocols);
}
public Port createPort(UUID id, Broker broker, Map<String, Object> attributes)
{
final Port port;
Map<String, Object> defaults = new HashMap<String, Object>();
defaults.put(Port.TRANSPORTS, Collections.singleton(DEFAULT_TRANSPORT));
Object portValue = attributes.get(Port.PORT);
if (portValue == null)
{
throw new IllegalConfigurationException("Port attribute is not specified for port: " + attributes);
}
Set<Protocol> protocols = MapValueConverter.getEnumSetAttribute(Port.PROTOCOLS, attributes, Protocol.class);
if (isAmqpProtocol(protocols, attributes))
{
Object binding = attributes.get(Port.BINDING_ADDRESS);
if (binding == null)
{
binding = DEFAULT_AMQP_BINDING;
defaults.put(Port.BINDING_ADDRESS, DEFAULT_AMQP_BINDING);
}
defaults.put(Port.NAME, binding + ":" + portValue);
defaults.put(Port.PROTOCOLS, _defaultProtocols);
defaults.put(Port.TCP_NO_DELAY, DEFAULT_AMQP_TCP_NO_DELAY);
defaults.put(Port.WANT_CLIENT_AUTH, DEFAULT_AMQP_WANT_CLIENT_AUTH);
defaults.put(Port.NEED_CLIENT_AUTH, DEFAULT_AMQP_NEED_CLIENT_AUTH);
defaults.put(Port.RECEIVE_BUFFER_SIZE, DEFAULT_AMQP_RECEIVE_BUFFER_SIZE);
defaults.put(Port.SEND_BUFFER_SIZE, DEFAULT_AMQP_SEND_BUFFER_SIZE);
port = new AmqpPortAdapter(id, broker, attributes, defaults, broker.getTaskExecutor());
boolean useClientAuth = (Boolean) port.getAttribute(Port.NEED_CLIENT_AUTH) || (Boolean) port.getAttribute(Port.WANT_CLIENT_AUTH);
if(useClientAuth && port.getTrustStores().isEmpty())
{
throw new IllegalConfigurationException("Can't create port which requests SSL client certificates but has no trust stores configured.");
}
if(useClientAuth && !port.getTransports().contains(Transport.SSL))
{
throw new IllegalConfigurationException("Can't create port which requests SSL client certificates but doesn't use SSL transport.");
}
}
else
{
if (protocols.size() > 1)
{
throw new IllegalConfigurationException("Only one protocol can be used on non AMQP port");
}
Protocol protocol = protocols.iterator().next();
if(!broker.isManagementMode() && protocol.getProtocolType() != ProtocolType.HTTP)
{
//ManagementMode needs this relaxed to allow its overriding management ports to be inserted.
//Enforce only a single port of each management protocol, as the plugins will only use one.
Collection<Port> existingPorts = broker.getPorts();
for (Port existingPort : existingPorts)
{
Collection<Protocol> portProtocols = existingPort.getProtocols();
if (portProtocols != null && portProtocols.contains(protocol))
{
throw new IllegalConfigurationException("Port for protocol " + protocol + " already exists. Only one management port per protocol can be created.");
}
}
}
defaults.put(Port.NAME, portValue + "-" + protocol.name());
port = new PortAdapter(id, broker, attributes, defaults, broker.getTaskExecutor());
boolean rmiPort = port.getProtocols().contains(Protocol.RMI);
if (rmiPort && port.getTransports().contains(Transport.SSL))
{
throw new IllegalConfigurationException("Can't create RMI registry port which requires SSL");
}
}
if(port.getTransports().contains(Transport.SSL))
{
if(port.getKeyStore() == null)
{
throw new IllegalConfigurationException("Can't create port which requires SSL but has no key store configured.");
}
}
return port;
}
private boolean isAmqpProtocol(Set<Protocol> protocols, Map<String, Object> portAttributes)
{
if (protocols == null || protocols.isEmpty())
{
// defaulting to AMQP if protocol is not specified
return true;
}
Set<ProtocolType> protocolTypes = new HashSet<ProtocolType>();
for (Protocol protocolObject : protocols)
{
protocolTypes.add(protocolObject.getProtocolType());
}
if (protocolTypes.size() > 1)
{
throw new IllegalConfigurationException("Found different protocol types '" + protocolTypes
+ "' for port configuration: " + portAttributes);
}
return protocolTypes.contains(ProtocolType.AMQP);
}
public Collection<Protocol> getDefaultProtocols()
{
return _defaultProtocols;
}
}