Author: kwall
Date: Sun Jul 17 20:05:03 2016
New Revision: 1753106

URL: http://svn.apache.org/viewvc?rev=1753106&view=rev
Log:
QPID-7327: [Java Broker] Improve state management within the redirecting 
virtualhostnode/virtualhost implementation.

Added:
    
qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostNodeTest.java
Modified:
    
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHost.java
    
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostImpl.java
    
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostNode.java
    
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostNodeImpl.java

Modified: 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHost.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHost.java?rev=1753106&r1=1753105&r2=1753106&view=diff
==============================================================================
--- 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHost.java
 (original)
+++ 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHost.java
 Sun Jul 17 20:05:03 2016
@@ -27,4 +27,8 @@ public interface RedirectingVirtualHost<
         extends VirtualHost<X>,
                 NonStandardVirtualHost<X>
 {
+    String CLASS_DESCRIPTION = "A special virtualhost type that merely 
redirects all incoming connections to an"
+                               + " alternative broker.  The mapping which 
governs the redirect is held by the"
+                               + " the parent virtual host node.";
+
 }

Modified: 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostImpl.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostImpl.java?rev=1753106&r1=1753105&r2=1753106&view=diff
==============================================================================
--- 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostImpl.java
 (original)
+++ 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostImpl.java
 Sun Jul 17 20:05:03 2016
@@ -32,6 +32,7 @@ import java.util.concurrent.ScheduledFut
 
 import com.google.common.util.concurrent.ListenableFuture;
 
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
 import org.apache.qpid.server.logging.EventLogger;
 import org.apache.qpid.server.message.MessageDestination;
 import org.apache.qpid.server.message.MessageSource;
@@ -55,12 +56,14 @@ import org.apache.qpid.server.transport.
 import org.apache.qpid.server.txn.DtxRegistry;
 import org.apache.qpid.server.virtualhost.*;
 
-@ManagedObject( category = false, type = RedirectingVirtualHostImpl.TYPE, 
register = false )
+@ManagedObject( category = false, type = 
RedirectingVirtualHostImpl.VIRTUAL_HOST_TYPE, register = false,
+                description = RedirectingVirtualHostImpl.CLASS_DESCRIPTION)
 class RedirectingVirtualHostImpl
     extends AbstractConfiguredObject<RedirectingVirtualHostImpl>
         implements RedirectingVirtualHost<RedirectingVirtualHostImpl>
 {
-    public static final String TYPE = "REDIRECTOR";
+    public static final String VIRTUAL_HOST_TYPE = "REDIRECTOR";
+
     private final StatisticsCounter _messagesDelivered, _dataDelivered, 
_messagesReceived, _dataReceived;
     private final Broker<?> _broker;
     private final VirtualHostPrincipal _principal;
@@ -128,7 +131,16 @@ class RedirectingVirtualHostImpl
     {
         super.validateChange(proxyForValidation, changedAttributes);
 
-        throwUnsupportedForRedirector();
+        if (changedAttributes.contains(DESIRED_STATE) && 
proxyForValidation.getDesiredState() == State.DELETED)
+        {
+            throw new IllegalConfigurationException("Directly deleting a 
redirecting virtualhost is not supported. "
+            + "Delete the parent virtual host node '" + 
getParent(VirtualHostNode.class) + "' instead.");
+        }
+        else
+        {
+            throw new IllegalConfigurationException("A redirecting virtualhost 
does not support changing of"
+                                                    + " its attributes");
+        }
     }
 
     @Override

Modified: 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostNode.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostNode.java?rev=1753106&r1=1753105&r2=1753106&view=diff
==============================================================================
--- 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostNode.java
 (original)
+++ 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostNode.java
 Sun Jul 17 20:05:03 2016
@@ -30,7 +30,10 @@ import org.apache.qpid.server.model.Virt
 @ManagedObject(type= RedirectingVirtualHostNodeImpl.VIRTUAL_HOST_NODE_TYPE, 
category=false, validChildTypes = 
"org.apache.qpid.server.virtualhostnode.RedirectingVirtualHostNodeImpl#getSupportedChildTypes()")
 public interface RedirectingVirtualHostNode<X extends 
RedirectingVirtualHostNode<X>> extends VirtualHostNode<X>
 {
+    public static final String REDIRECTS = "redirects";
 
-    @ManagedAttribute( defaultValue = "{}")
+    @ManagedAttribute( defaultValue = "{}", description = "Port to hostname 
mapping.  Connections made to this virtual"
+                                                          + " host node on a 
mapped port will be automatically"
+                                                          + " redirected to 
the given hostname.")
     Map<Port<?>, String> getRedirects();
 }

Modified: 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostNodeImpl.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostNodeImpl.java?rev=1753106&r1=1753105&r2=1753106&view=diff
==============================================================================
--- 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostNodeImpl.java
 (original)
+++ 
qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostNodeImpl.java
 Sun Jul 17 20:05:03 2016
@@ -22,11 +22,15 @@ package org.apache.qpid.server.virtualho
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.Callable;
 
+import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -66,7 +70,7 @@ public class RedirectingVirtualHostNodeI
     @ManagedAttributeField
     private Map<Port<?>,String> _redirects;
 
-    private RedirectingVirtualHostImpl _virtualHost;
+    private volatile RedirectingVirtualHostImpl _virtualHost;
 
     @ManagedObjectFactoryConstructor
     public RedirectingVirtualHostNodeImpl(Map<String, Object> attributes, 
Broker<?> parent)
@@ -74,31 +78,121 @@ public class RedirectingVirtualHostNodeI
         super(Collections.<Class<? extends 
ConfiguredObject>,ConfiguredObject<?>>singletonMap(Broker.class, parent),
               attributes);
         _broker = parent;
-
     }
 
     @StateTransition( currentState = {State.UNINITIALIZED, State.STOPPED, 
State.ERRORED }, desiredState = State.ACTIVE )
-    protected ListenableFuture<Void> doActivate()
+    private ListenableFuture<Void> doActivate()
     {
-        try
-        {
-            _virtualHost = new 
RedirectingVirtualHostImpl(Collections.<String,Object>singletonMap(ConfiguredObject.NAME,getName()),
 this);
-            _virtualHost.create();
-            setState(State.ACTIVE);
-        }
-        catch(RuntimeException e)
+        final SettableFuture<Void> resultFuture = SettableFuture.create();
+        Map<String, Object> attributes = new HashMap<>();
+        attributes.put(ConfiguredObject.NAME, getName());
+        attributes.put(ConfiguredObject.TYPE, 
RedirectingVirtualHostImpl.VIRTUAL_HOST_TYPE);
+
+        final ListenableFuture<VirtualHost> virtualHostFuture = 
getObjectFactory().createAsync(VirtualHost.class, attributes, this);
+
+        Futures.addCallback(virtualHostFuture, new 
FutureCallback<VirtualHost>()
         {
-            setState(State.ERRORED);
-            if (getParent(Broker.class).isManagementMode())
+            @Override
+            public void onSuccess(final VirtualHost virtualHost)
             {
-                LOGGER.warn("Failed to make " + this + " active.", e);
+                _virtualHost = (RedirectingVirtualHostImpl) virtualHost;
+                setState(State.ACTIVE);
+                resultFuture.set(null);
+
             }
-            else
+
+            @Override
+            public void onFailure(final Throwable t)
             {
-                throw e;
+                setState(State.ERRORED);
+                if (getParent(Broker.class).isManagementMode())
+                {
+                    LOGGER.warn("Failed to make {} active.", this, t);
+                    resultFuture.set(null);
+                }
+                else
+                {
+                    resultFuture.setException(t);
+                }
             }
+        });
+
+        return resultFuture;
+    }
+
+    @StateTransition( currentState = { State.ACTIVE, State.STOPPED, 
State.ERRORED}, desiredState = State.DELETED )
+    private ListenableFuture<Void> doDelete()
+    {
+        final ListenableFuture<Void> future = Futures.immediateFuture(null);
+        final RedirectingVirtualHostImpl virtualHost = _virtualHost;
+        if (virtualHost != null)
+        {
+            return doAfter(virtualHost.closeAsync(), new 
Callable<ListenableFuture<Void>>()
+            {
+                @Override
+                public ListenableFuture<Void> call() throws Exception
+                {
+                    _virtualHost = null;
+                    deleted();
+                    setState(State.DELETED);
+                    return future;
+                }
+            });
+        }
+        else
+        {
+            setState(State.DELETED);
+            deleted();
+            return future;
+        }
+    }
+
+    @StateTransition( currentState = { State.ACTIVE, State.ERRORED, 
State.UNINITIALIZED }, desiredState = State.STOPPED )
+    private ListenableFuture<Void> doStop()
+    {
+        final ListenableFuture<Void> future = Futures.immediateFuture(null);
+        final RedirectingVirtualHostImpl virtualHost = _virtualHost;
+        if (virtualHost != null)
+        {
+            return doAfter(virtualHost.closeAsync(), new 
Callable<ListenableFuture<Void>>()
+            {
+                @Override
+                public ListenableFuture<Void> call() throws Exception
+                {
+                    _virtualHost = null;
+                    setState(State.STOPPED);
+                    return future;
+                }
+            });
+        }
+        else
+        {
+            setState(State.STOPPED);
+            return future;
+        }
+    }
+
+    @Override
+    protected ListenableFuture<Void> beforeClose()
+    {
+        final ListenableFuture<Void> superFuture = super.beforeClose();
+        final RedirectingVirtualHostImpl virtualHost = _virtualHost;
+        if (virtualHost != null)
+        {
+            return doAfter(virtualHost.closeAsync(), new 
Callable<ListenableFuture<Void>>()
+            {
+                @Override
+                public ListenableFuture<Void> call() throws Exception
+                {
+                    _virtualHost = null;
+                    return superFuture;
+                }
+            });
+        }
+        else
+        {
+            return superFuture;
         }
-        return Futures.immediateFuture(null);
     }
 
     @Override
@@ -149,7 +243,6 @@ public class RedirectingVirtualHostNodeI
         return _redirects;
     }
 
-
     @Override
     protected void validateOnCreate()
     {
@@ -165,7 +258,6 @@ public class RedirectingVirtualHostNodeI
                                                       + "' is already the 
default for the Broker.");
             }
         }
-
     }
 
     @Override
@@ -186,6 +278,19 @@ public class RedirectingVirtualHostNodeI
         }
     }
 
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Override
+    protected <C extends ConfiguredObject> ListenableFuture<C> 
addChildAsync(Class<C> childClass, Map<String, Object> attributes,
+                                                                             
ConfiguredObject... otherParents)
+    {
+        if(childClass == VirtualHost.class)
+        {
+            throw new UnsupportedOperationException("The redirecting 
virtualhost node automatically manages the creation"
+                                                    + " of the redirecting 
virtualhost. Creating it explicitly is not supported.");
+        }
+        return super.addChildAsync(childClass, attributes, otherParents);
+    }
+
     public static Map<String, Collection<String>> getSupportedChildTypes()
     {
         Collection<String> validVhostTypes = 
Collections.singleton(RedirectingVirtualHostImpl.TYPE);

Added: 
qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostNodeTest.java
URL: 
http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostNodeTest.java?rev=1753106&view=auto
==============================================================================
--- 
qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostNodeTest.java
 (added)
+++ 
qpid/java/trunk/broker-core/src/test/java/org/apache/qpid/server/virtualhostnode/RedirectingVirtualHostNodeTest.java
 Sun Jul 17 20:05:03 2016
@@ -0,0 +1,154 @@
+/*
+ *
+ * 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.virtualhostnode;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.qpid.server.configuration.updater.CurrentThreadTaskExecutor;
+import org.apache.qpid.server.configuration.updater.TaskExecutor;
+import org.apache.qpid.server.model.AuthenticationProvider;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.BrokerTestHelper;
+import org.apache.qpid.server.model.ConfiguredObjectFactoryImpl;
+import org.apache.qpid.server.model.Model;
+import org.apache.qpid.server.model.Port;
+import org.apache.qpid.server.model.SystemConfig;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.model.VirtualHostNode;
+import org.apache.qpid.server.model.port.AmqpPort;
+import org.apache.qpid.test.utils.QpidTestCase;
+
+public class RedirectingVirtualHostNodeTest extends QpidTestCase
+{
+    private static final String TEST_VIRTUAL_HOST_NODE_NAME = "testNode";
+
+    private final UUID _nodeId = UUID.randomUUID();
+    private Broker<?> _broker;
+    private TaskExecutor _taskExecutor;
+    private AmqpPort _port;
+
+    @Override
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+
+        _broker = BrokerTestHelper.createBrokerMock();
+        SystemConfig<?> systemConfig = _broker.getParent(SystemConfig.class);
+        when(systemConfig.getObjectFactory()).thenReturn(new 
ConfiguredObjectFactoryImpl(mock(Model.class)));
+
+        _taskExecutor = new CurrentThreadTaskExecutor();
+        _taskExecutor.start();
+        when(_broker.getTaskExecutor()).thenReturn(_taskExecutor);
+        when(_broker.getChildExecutor()).thenReturn(_taskExecutor);
+
+        AuthenticationProvider dummyAuthProvider = 
mock(AuthenticationProvider.class);
+        when(dummyAuthProvider.getName()).thenReturn("dummy");
+        when(dummyAuthProvider.getId()).thenReturn(UUID.randomUUID());
+        
when(dummyAuthProvider.getMechanisms()).thenReturn(Arrays.asList("PLAIN"));
+        
when(_broker.getChildren(eq(AuthenticationProvider.class))).thenReturn(Collections.singleton(dummyAuthProvider));
+
+        final Map<String, Object> attributes = new HashMap<>();
+        attributes.put(Port.NAME, getTestName());
+        attributes.put(Port.PORT, 0);
+        attributes.put(Port.AUTHENTICATION_PROVIDER, "dummy");
+        attributes.put(Port.TYPE, "AMQP");
+        _port = (AmqpPort) _broker.getObjectFactory().create(Port.class, 
attributes, _broker);
+    }
+
+    @Override
+    protected void tearDown() throws Exception
+    {
+        try
+        {
+            _taskExecutor.stopImmediately();
+        }
+        finally
+        {
+            super.tearDown();
+        }
+    }
+
+    public void testRedirectingVHNHasRedirectingVHToo() throws Exception
+    {
+        final Map<String, Object> attributes = 
createVirtualHostNodeAttributes();
+
+        RedirectingVirtualHostNode node =
+                (RedirectingVirtualHostNode) 
_broker.getObjectFactory().create(VirtualHostNode.class,
+                                                                               
attributes,
+                                                                               
_broker);
+        assertEquals("Unexpected number of virtualhost children",
+                     1, node.getChildren(VirtualHost.class).size());
+        assertTrue("Virtualhost child is of unexpected type",
+                   node.getChildren(VirtualHost.class).iterator().next() 
instanceof RedirectingVirtualHost);
+    }
+
+    public void testStopAndRestartVHN() throws Exception
+    {
+        final Map<String, Object> attributes = 
createVirtualHostNodeAttributes();
+
+        RedirectingVirtualHostNode node =
+                (RedirectingVirtualHostNode) 
_broker.getObjectFactory().create(VirtualHostNode.class,
+                                                                               
attributes,
+                                                                               
_broker);
+        assertEquals("Unexpected number of virtualhost children before stop",
+                     1, node.getChildren(VirtualHost.class).size());
+        node.stop();
+        assertEquals("Unexpected number of virtualhost children after stop",
+                     0, node.getChildren(VirtualHost.class).size());
+        node.start();
+        assertEquals("Unexpected number of virtualhost children after restart",
+                     1, node.getChildren(VirtualHost.class).size());
+    }
+
+    public void testDeleteVHN() throws Exception
+    {
+        final Map<String, Object> attributes = 
createVirtualHostNodeAttributes();
+
+        RedirectingVirtualHostNode node =
+                (RedirectingVirtualHostNode) 
_broker.getObjectFactory().create(VirtualHostNode.class,
+                                                                               
attributes,
+                                                                               
_broker);
+        assertEquals("Unexpected number of virtualhost children before delete",
+                     1, node.getChildren(VirtualHost.class).size());
+        node.delete();
+        assertEquals("Unexpected number of virtualhost children after delete",
+                     0, node.getChildren(VirtualHost.class).size());
+    }
+
+    private Map<String, Object> createVirtualHostNodeAttributes()
+    {
+        final Map<String, Object> attributes = new HashMap<>();
+        attributes.put(VirtualHostNode.TYPE, 
RedirectingVirtualHostNodeImpl.VIRTUAL_HOST_NODE_TYPE);
+        attributes.put(VirtualHostNode.NAME, TEST_VIRTUAL_HOST_NODE_NAME);
+        attributes.put(RedirectingVirtualHostNode.REDIRECTS, 
Collections.singletonMap(_port, "myalternativehostname"));
+        return attributes;
+    }
+
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to