blob: 8ea5510ce6f2710208893c491c2567a96b60c315 [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.logging.actors;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.server.logging.LogActor;
import org.apache.qpid.server.logging.NullRootMessageLogger;
/**
* Test : CurrentActorTest
* Summary:
* Validate ThreadLocal operation.
*
* Test creates THREADS number of threads which all then execute the same test
* together ( as close as looping Thread.start() will allow).
*
* Test:
* Test sets the CurrentActor then proceeds to retrieve the value and use it.
*
* The test also validates that it is the same LogActor that this thread set.
*
* Finally the LogActor is removed and tested to make sure that it was
* successfully removed.
*
* By having a higher number of threads than would normally be used in the
* Poolling filter we aim to catch the race condition where a ThreadLocal remove
* is called before one or more threads call get(). This way we can ensure that
* the remove does not affect more than the Thread it was called in.
*/
public class CurrentActorTest extends BaseConnectionActorTestCase
{
//Set this to be a reasonably large number
private static final int THREADS = 10;
/**
* Test that CurrentActor behaves as LIFO queue.
*
* Test creates two Actors Connection and Channel and then sets the
* CurrentActor.
*
* The test validates that CurrentActor remembers the Connection actor
* after the Channel actor has been removed.
*
* And then finally validates that removing the Connection actor results
* in there being no actors set.
*
* @throws AMQException
* @throws org.apache.commons.configuration.ConfigurationException
*/
public void testLIFO() throws AMQException, ConfigurationException
{
assertTrue("Unexpected actor: " + CurrentActor.get(), CurrentActor.get() instanceof TestLogActor);
AMQPConnectionActor connectionActor = new AMQPConnectionActor(getSession(),
new NullRootMessageLogger());
/*
* Push the actor on to the stack:
*
* CurrentActor -> Connection
* Stack -> null
*/
CurrentActor.set(connectionActor);
//Use the Actor to send a simple message
sendTestLogMessage(CurrentActor.get());
// Verify it was the same actor as we set earlier
assertEquals("Retrieved actor is not as expected ",
connectionActor, CurrentActor.get());
/**
* Set the actor to now be the Channel actor so testing the ability
* to push the actor on to the stack:
*
* CurrentActor -> Channel
* Stack -> Connection, null
*
*/
AMQChannel channel = new AMQChannel(getSession(), 1, getSession().getVirtualHost().getMessageStore());
AMQPChannelActor channelActor = new AMQPChannelActor(channel,
new NullRootMessageLogger());
CurrentActor.set(channelActor);
//Use the Actor to send a simple message
sendTestLogMessage(CurrentActor.get());
// Verify it was the same actor as we set earlier
assertEquals("Retrieved actor is not as expected ",
channelActor, CurrentActor.get());
// Remove the ChannelActor from the stack
CurrentActor.remove();
/*
* Pop the actor on to the stack:
*
* CurrentActor -> Connection
* Stack -> null
*/
// Verify we now have the same connection actor as we set earlier
assertEquals("Retrieved actor is not as expected ",
connectionActor, CurrentActor.get());
// Verify that removing the our last actor it returns us to the test
// default that the ApplicationRegistry sets.
CurrentActor.remove();
/*
* Pop the actor on to the stack:
*
* CurrentActor -> null
*/
assertEquals("CurrentActor not the Test default", TestLogActor.class ,CurrentActor.get().getClass());
}
/**
* Test the setting CurrentActor is done correctly as a ThreadLocal.
*
* The test starts 'THREADS' threads that all set the CurrentActor log
* a message then remove the actor.
*
* Checks are done to ensure that there is no set actor after the remove.
*
* If the ThreadLocal was not working then having concurrent actor sets
* would result in more than one actor and so the remove will not result
* in the clearing of the CurrentActor
*
*/
public void testThreadLocal()
{
// Setup the threads
LogMessagesWithAConnectionActor[] threads = new LogMessagesWithAConnectionActor[THREADS];
for (int count = 0; count < THREADS; count++)
{
threads[count] = new LogMessagesWithAConnectionActor();
}
//Run the threads
for (int count = 0; count < THREADS; count++)
{
threads[count].start();
}
// Wait for them to finish
for (int count = 0; count < THREADS; count++)
{
try
{
threads[count].join();
}
catch (InterruptedException e)
{
//if we are interrupted then we will exit shortly.
}
}
// Verify that none of the tests threw an exception
for (int count = 0; count < THREADS; count++)
{
if (threads[count].getException() != null)
{
threads[count].getException().printStackTrace();
fail("Error occured in thread:" + count + "("+threads[count].getException()+")");
}
}
}
/**
* Creates a new ConnectionActor and logs the given number of messages
* before removing the actor and validating that there is no set actor.
*/
public class LogMessagesWithAConnectionActor extends Thread
{
private Throwable _exception;
public LogMessagesWithAConnectionActor()
{
}
public void run()
{
// Create a new actor using retrieving the rootMessageLogger from
// the default ApplicationRegistry.
//fixme reminder that we need a better approach for broker testing.
try
{
LogActor defaultActor = CurrentActor.get();
AMQPConnectionActor actor = new AMQPConnectionActor(getSession(),
new NullRootMessageLogger());
CurrentActor.set(actor);
//Use the Actor to send a simple message
sendTestLogMessage(CurrentActor.get());
// Verify it was the same actor as we set earlier
if(!actor.equals(CurrentActor.get()))
{
throw new IllegalArgumentException("Retrieved actor is not as expected ");
}
// Verify that removing the actor works for this thread
CurrentActor.remove();
if(CurrentActor.get() != defaultActor)
{
throw new IllegalArgumentException("CurrentActor ("+CurrentActor.get()+") should be default actor" + defaultActor);
}
}
catch (Throwable e)
{
_exception = e;
}
}
public Throwable getException()
{
return _exception;
}
}
}