summaryrefslogtreecommitdiff
path: root/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AbstractPort.java
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AbstractPort.java')
-rw-r--r--qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AbstractPort.java595
1 files changed, 595 insertions, 0 deletions
diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AbstractPort.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AbstractPort.java
new file mode 100644
index 0000000000..e758e1a1dd
--- /dev/null
+++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/port/AbstractPort.java
@@ -0,0 +1,595 @@
+/*
+ *
+ * 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.port;
+
+import java.lang.reflect.Type;
+import java.security.AccessControlException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.qpid.server.model.*;
+import org.apache.qpid.server.security.access.Operation;
+import org.apache.qpid.server.util.MapValueConverter;
+import org.apache.qpid.server.util.ParameterizedTypeImpl;
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.server.configuration.updater.TaskExecutor;
+
+abstract public class AbstractPort<X extends AbstractPort<X>> extends AbstractConfiguredObject<X> implements Port<X>
+{
+ @SuppressWarnings("serial")
+ public static final Map<String, Type> ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap<String, Type>(){{
+ put(NAME, String.class);
+ put(PROTOCOLS, new ParameterizedTypeImpl(Set.class, Protocol.class));
+ put(TRANSPORTS, new ParameterizedTypeImpl(Set.class, Transport.class));
+ put(TRUST_STORES, new ParameterizedTypeImpl(Set.class, String.class));
+ put(KEY_STORE, String.class);
+ put(PORT, Integer.class);
+ put(TCP_NO_DELAY, Boolean.class);
+ put(RECEIVE_BUFFER_SIZE, Integer.class);
+ put(SEND_BUFFER_SIZE, Integer.class);
+ put(NEED_CLIENT_AUTH, Boolean.class);
+ put(WANT_CLIENT_AUTH, Boolean.class);
+ put(BINDING_ADDRESS, String.class);
+ put(STATE, State.class);
+ put(AUTHENTICATION_PROVIDER, String.class);
+ }});
+
+ public static final Transport DEFAULT_TRANSPORT = Transport.TCP;
+
+ private final Broker<?> _broker;
+ private AtomicReference<State> _state;
+
+ public AbstractPort(UUID id,
+ Broker<?> broker,
+ Map<String, Object> attributes,
+ Map<String, Object> defaults,
+ TaskExecutor taskExecutor)
+ {
+ super(Collections.<Class<? extends ConfiguredObject>,ConfiguredObject<?>>singletonMap(Broker.class, broker),
+ updateDefaults(defaults, attributes),
+ combineIdWithAttributes(id,MapValueConverter.convert(attributes, ATTRIBUTE_TYPES)),
+ taskExecutor);
+ _broker = broker;
+
+ Object portValue = attributes.get(Port.PORT);
+ if (portValue == null)
+ {
+ throw new IllegalConfigurationException("Port attribute is not specified for port: " + attributes);
+ }
+
+ State state = MapValueConverter.getEnumAttribute(State.class, STATE, attributes, State.INITIALISING);
+ _state = new AtomicReference<State>(state);
+
+
+ boolean useClientAuth = Boolean.TRUE.equals(getAttribute(Port.NEED_CLIENT_AUTH))
+ || Boolean.TRUE.equals(getAttribute(Port.WANT_CLIENT_AUTH));
+
+ if(useClientAuth && getTrustStores().isEmpty())
+ {
+ throw new IllegalConfigurationException("Can't create port which requests SSL client certificates but has no trust stores configured.");
+ }
+
+ boolean useTLSTransport = getTransports().contains(Transport.SSL) || getTransports().contains(Transport.WSS);
+ if(useClientAuth && !useTLSTransport)
+ {
+ throw new IllegalConfigurationException(
+ "Can't create port which requests SSL client certificates but doesn't use SSL transport.");
+ }
+ if(useTLSTransport && getKeyStore() == null)
+ {
+ throw new IllegalConfigurationException("Can't create a port which uses a secure transport but has no KeyStore");
+ }
+ }
+
+
+ private static Map<String, Object> updateDefaults(final Map<String, Object> defaults,
+ final Map<String, Object> attributes)
+ {
+ Map<String, Object> updatedDefaults = new HashMap<String, Object>(defaults);
+ if(!defaults.containsKey(TRANSPORTS))
+ {
+ updatedDefaults.put(Port.TRANSPORTS, Collections.singleton(DEFAULT_TRANSPORT));
+ }
+ if(!defaults.containsKey(NAME))
+ {
+ updatedDefaults.put(NAME, attributes.get(PORT) + "-" + attributes.get(TYPE));
+ }
+ return updatedDefaults;
+ }
+
+ @Override
+ public String getBindingAddress()
+ {
+ return (String)getAttribute(BINDING_ADDRESS);
+ }
+
+ @Override
+ public int getPort()
+ {
+ return (Integer)getAttribute(PORT);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Collection<Transport> getTransports()
+ {
+ return (Collection<Transport>)getAttribute(TRANSPORTS);
+ }
+
+ @Override
+ public void addTransport(Transport transport)
+ throws IllegalStateException, AccessControlException, IllegalArgumentException
+ {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public Transport removeTransport(Transport transport)
+ throws IllegalStateException, AccessControlException, IllegalArgumentException
+ {
+ throw new IllegalStateException();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Collection<Protocol> getProtocols()
+ {
+ return (Collection<Protocol>)getAttribute(PROTOCOLS);
+ }
+
+ @Override
+ public void addProtocol(Protocol protocol)
+ throws IllegalStateException, AccessControlException, IllegalArgumentException
+ {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public Protocol removeProtocol(Protocol protocol)
+ throws IllegalStateException, AccessControlException, IllegalArgumentException
+ {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public Collection<VirtualHostAlias> getVirtualHostBindings()
+ {
+ List<VirtualHostAlias> aliases = new ArrayList<VirtualHostAlias>();
+ for(VirtualHost<?> vh : _broker.getVirtualHosts())
+ {
+ for(VirtualHostAlias<?> alias : vh.getAliases())
+ {
+ if(alias.getPort().equals(this))
+ {
+ aliases.add(alias);
+ }
+ }
+ }
+ return Collections.unmodifiableCollection(aliases);
+ }
+
+ @Override
+ public Collection<Connection> getConnections()
+ {
+ return null;
+ }
+
+ @Override
+ public String setName(String currentName, String desiredName) throws IllegalStateException, AccessControlException
+ {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public State getState()
+ {
+ return _state.get();
+ }
+
+ @Override
+ public boolean isDurable()
+ {
+ return false;
+ }
+
+ @Override
+ public void setDurable(boolean durable)
+ throws IllegalStateException, AccessControlException, IllegalArgumentException
+ {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public LifetimePolicy getLifetimePolicy()
+ {
+ return LifetimePolicy.PERMANENT;
+ }
+
+ @Override
+ public LifetimePolicy setLifetimePolicy(LifetimePolicy expected, LifetimePolicy desired)
+ throws IllegalStateException, AccessControlException, IllegalArgumentException
+ {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public <C extends ConfiguredObject> Collection<C> getChildren(Class<C> clazz)
+ {
+ if(clazz == Connection.class)
+ {
+ return (Collection<C>) getConnections();
+ }
+ else
+ {
+ return Collections.emptySet();
+ }
+ }
+
+ @Override
+ public Object getAttribute(String name)
+ {
+ if(ID.equals(name))
+ {
+ return getId();
+ }
+ else if(STATE.equals(name))
+ {
+ return getState();
+ }
+ else if(DURABLE.equals(name))
+ {
+ return isDurable();
+ }
+ else if(LIFETIME_POLICY.equals(name))
+ {
+ return getLifetimePolicy();
+ }
+ return super.getAttribute(name);
+ }
+
+ @Override
+ public Collection<String> getAttributeNames()
+ {
+ return getAttributeNames(getClass());
+ }
+
+ @Override
+ public boolean setState(State currentState, State desiredState)
+ {
+ State state = _state.get();
+ if (desiredState == State.DELETED)
+ {
+ if (state == State.INITIALISING || state == State.ACTIVE || state == State.STOPPED || state == State.QUIESCED || state == State.ERRORED)
+ {
+ if( _state.compareAndSet(state, State.DELETED))
+ {
+ onStop();
+ return true;
+ }
+ }
+ else
+ {
+ throw new IllegalStateException("Cannot delete port in " + state + " state");
+ }
+ }
+ else if (desiredState == State.ACTIVE)
+ {
+ if ((state == State.INITIALISING || state == State.QUIESCED) && _state.compareAndSet(state, State.ACTIVE))
+ {
+ try
+ {
+ onActivate();
+ }
+ catch(RuntimeException e)
+ {
+ _state.compareAndSet(State.ACTIVE, State.ERRORED);
+ throw e;
+ }
+ return true;
+ }
+ else
+ {
+ throw new IllegalStateException("Cannot activate port in " + state + " state");
+ }
+ }
+ else if (desiredState == State.QUIESCED)
+ {
+ if (state == State.INITIALISING && _state.compareAndSet(state, State.QUIESCED))
+ {
+ return true;
+ }
+ }
+ else if (desiredState == State.STOPPED)
+ {
+ if (_state.compareAndSet(state, State.STOPPED))
+ {
+ onStop();
+ return true;
+ }
+ else
+ {
+ throw new IllegalStateException("Cannot stop port in " + state + " state");
+ }
+ }
+ return false;
+ }
+
+ protected void onActivate()
+ {
+ // no-op: expected to be overridden by subclass
+ }
+
+ protected void onStop()
+ {
+ // no-op: expected to be overridden by subclass
+ }
+
+ @Override
+ protected void changeAttributes(Map<String, Object> attributes)
+ {
+ Map<String, Object> converted = MapValueConverter.convert(attributes, ATTRIBUTE_TYPES);
+
+ Map<String, Object> merged = generateEffectiveAttributes(converted);
+
+ String newName = (String) merged.get(NAME);
+ if(!getName().equals(newName))
+ {
+ throw new IllegalConfigurationException("Changing the port name is not allowed");
+ }
+
+ Integer newPort = (Integer) merged.get(PORT);
+ if(getPort() != newPort)
+ {
+ for(Port p : _broker.getPorts())
+ {
+ if(p.getPort() == newPort)
+ {
+ throw new IllegalConfigurationException("Port number " + newPort + " is already in use by port " + p.getName());
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ Collection<Transport> transports = (Collection<Transport>)merged.get(TRANSPORTS);
+ @SuppressWarnings("unchecked")
+ Collection<Protocol> protocols = (Collection<Protocol>)merged.get(PROTOCOLS);
+ Boolean needClientCertificate = (Boolean)merged.get(NEED_CLIENT_AUTH);
+ Boolean wantClientCertificate = (Boolean)merged.get(WANT_CLIENT_AUTH);
+ boolean requiresCertificate = (needClientCertificate != null && needClientCertificate.booleanValue())
+ || (wantClientCertificate != null && wantClientCertificate.booleanValue());
+
+ String keyStoreName = (String) merged.get(KEY_STORE);
+ if(keyStoreName != null)
+ {
+ if (_broker.findKeyStoreByName(keyStoreName) == null)
+ {
+ throw new IllegalConfigurationException("Can't find key store with name '" + keyStoreName + "' for port " + getName());
+ }
+ }
+
+ Set<String> trustStoreNames = (Set<String>) merged.get(TRUST_STORES);
+ boolean hasTrustStore = trustStoreNames != null && !trustStoreNames.isEmpty();
+ if(hasTrustStore)
+ {
+ for (String trustStoreName : trustStoreNames)
+ {
+ if (_broker.findTrustStoreByName(trustStoreName) == null)
+ {
+ throw new IllegalConfigurationException("Cannot find trust store with name '" + trustStoreName + "'");
+ }
+ }
+ }
+
+ boolean usesSsl = transports != null && transports.contains(Transport.SSL);
+ if (usesSsl)
+ {
+ if (keyStoreName == null)
+ {
+ throw new IllegalConfigurationException("Can't create port which requires SSL but has no key store configured.");
+ }
+
+ if (!hasTrustStore && requiresCertificate)
+ {
+ throw new IllegalConfigurationException("Can't create port which requests SSL client certificates but has no trust store configured.");
+ }
+ }
+ else
+ {
+ if (requiresCertificate)
+ {
+ throw new IllegalConfigurationException("Can't create port which requests SSL client certificates but doesn't use SSL transport.");
+ }
+ }
+
+ if (protocols != null && protocols.contains(Protocol.RMI) && usesSsl)
+ {
+ throw new IllegalConfigurationException("Can't create RMI Registry port which requires SSL.");
+ }
+
+ String authenticationProviderName = (String)merged.get(AUTHENTICATION_PROVIDER);
+ if (authenticationProviderName != null)
+ {
+ Collection<AuthenticationProvider<?>> providers = _broker.getAuthenticationProviders();
+ AuthenticationProvider<?> provider = null;
+ for (AuthenticationProvider<?> p : providers)
+ {
+ if (p.getName().equals(authenticationProviderName))
+ {
+ provider = p;
+ break;
+ }
+ }
+
+ if (provider == null)
+ {
+ throw new IllegalConfigurationException("Cannot find authentication provider with name '"
+ + authenticationProviderName + "'");
+ }
+ }
+ else
+ {
+ if (protocols != null && !protocols.contains(Protocol.RMI))
+ {
+ throw new IllegalConfigurationException("An authentication provider must be specified");
+ }
+ }
+
+ super.changeAttributes(converted);
+ }
+
+ @Override
+ protected void authoriseSetDesiredState(State currentState, State desiredState) throws AccessControlException
+ {
+ if(desiredState == State.DELETED)
+ {
+ if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), Port.class, Operation.DELETE))
+ {
+ throw new AccessControlException("Deletion of port is denied");
+ }
+ }
+ }
+
+ @Override
+ protected void authoriseSetAttribute(String name, Object expected, Object desired) throws AccessControlException
+ {
+ if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), Port.class, Operation.UPDATE))
+ {
+ throw new AccessControlException("Setting of port attributes is denied");
+ }
+ }
+
+ @Override
+ protected void authoriseSetAttributes(Map<String, Object> attributes) throws AccessControlException
+ {
+ if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), Port.class, Operation.UPDATE))
+ {
+ throw new AccessControlException("Setting of port attributes is denied");
+ }
+ }
+
+ @Override
+ public KeyStore getKeyStore()
+ {
+ String keyStoreName = (String)getAttribute(Port.KEY_STORE);
+ KeyStore keyStore = _broker.findKeyStoreByName(keyStoreName);
+
+ if (keyStoreName != null && keyStore == null)
+ {
+ throw new IllegalConfigurationException("Can't find key store with name '" + keyStoreName + "' for port " + getName());
+ }
+
+ return keyStore;
+ }
+
+ @Override
+ public Collection<TrustStore> getTrustStores()
+ {
+ Set<String> trustStoreNames = (Set<String>) getAttribute(TRUST_STORES);
+ boolean hasTrustStoreName = trustStoreNames != null && !trustStoreNames.isEmpty();
+
+ final Collection<TrustStore> trustStores = new ArrayList<TrustStore>();
+ if(hasTrustStoreName)
+ {
+ for (String name : trustStoreNames)
+ {
+ TrustStore trustStore = _broker.findTrustStoreByName(name);
+ if (trustStore == null)
+ {
+ throw new IllegalConfigurationException("Can't find trust store with name '" + name + "' for port " + getName());
+ }
+
+ trustStores.add(trustStore);
+ }
+ }
+
+ return trustStores;
+ }
+
+ @Override
+ public String toString()
+ {
+ return getClass().getSimpleName() + " [id=" + getId() + ", name=" + getName() + ", port=" + getPort() + "]";
+ }
+
+ @Override
+ public boolean isTcpNoDelay()
+ {
+ return (Boolean)getAttribute(TCP_NO_DELAY);
+ }
+
+ @Override
+ public int getSendBufferSize()
+ {
+ return (Integer)getAttribute(SEND_BUFFER_SIZE);
+ }
+
+ @Override
+ public int getReceiveBufferSize()
+ {
+ return (Integer)getAttribute(RECEIVE_BUFFER_SIZE);
+ }
+
+ @Override
+ public boolean getNeedClientAuth()
+ {
+ return (Boolean)getAttribute(NEED_CLIENT_AUTH);
+ }
+
+ @Override
+ public boolean getWantClientAuth()
+ {
+ return (Boolean)getAttribute(WANT_CLIENT_AUTH);
+ }
+
+ protected void validateOnlyOneInstance(final Broker<?> broker)
+ {
+ if(!broker.isManagementMode())
+ {
+ //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();
+ existingPorts.remove(this);
+
+ for (Port<?> existingPort : existingPorts)
+ {
+ Collection<Protocol> portProtocols = existingPort.getProtocols();
+ if (portProtocols != null)
+ {
+ final ArrayList<Protocol> intersection = new ArrayList(portProtocols);
+ intersection.retainAll(getProtocols());
+ if(!intersection.isEmpty())
+ {
+ throw new IllegalConfigurationException("Port for protocols " + intersection + " already exists. Only one management port per protocol can be created.");
+ }
+ }
+ }
+ }
+ }
+}