blob: 81ac693233e0567a47173481ed2e945f9143c2ec [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.felix.cm.integration;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Dictionary;
import junit.framework.TestCase;
import org.apache.felix.cm.integration.helper.ConfigureThread;
import org.apache.felix.cm.integration.helper.ManagedServiceFactoryThread;
import org.apache.felix.cm.integration.helper.ManagedServiceThread;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
/**
* The <code>ConfigUpdateStressTest</code> class tests the issues related to
* concurrency between configuration update (Configuration.update(Dictionary))
* and ManagedService[Factory] registration.
* <p>
* @see <a href="https://issues.apache.org/jira/browse/FELIX-1545">FELIX-1545</a>
*/
@RunWith(JUnit4TestRunner.class)
public class ConfigUpdateStressTest extends ConfigurationTestBase
{
@Test
public void test_ManagedService_race_condition_test()
{
int counterMax = 30;
int failures = 0;
for ( int counter = 0; counter < counterMax; counter++ )
{
try
{
single_test_ManagedService_race_condition_test( counter );
}
catch ( Throwable ae )
{
System.out.println( "single_test_ManagedService_race_condition_test#" + counter + " failed: " + ae );
ae.printStackTrace( System.out );
failures++;
}
}
// fail the test if there is at least one failure
if ( failures != 0 )
{
TestCase.fail( failures + "/" + counterMax + " iterations failed" );
}
}
@Test
public void test_ManagedServiceFactory_race_condition_test()
{
int counterMax = 30;
int failures = 0;
for ( int counter = 0; counter < counterMax; counter++ )
{
try
{
single_test_ManagedServiceFactory_race_condition_test( counter );
}
catch ( Throwable ae )
{
System.out.println( "single_test_ManagedServiceFactory_race_condition_test#" + counter + " failed: "
+ ae );
ae.printStackTrace( System.out );
failures++;
}
}
// fail the test if there is at least one failure
if ( failures != 0 )
{
TestCase.fail( failures + "/" + counterMax + " iterations failed" );
}
}
// runs a single test to encounter the race condition between ManagedService
// registration and Configuration.update(Dictionary)
// This test creates/updates configuration and registers a ManagedService
// almost at the same time. The ManagedService must receive the
// configuration
// properties exactly once.
private void single_test_ManagedService_race_condition_test( final int counter ) throws IOException,
InterruptedException
{
final String pid = "single_test_ManagedService_race_condition_test." + counter;
final ConfigureThread ct = new ConfigureThread( getConfigurationAdmin(), pid, false );
final ManagedServiceThread mt = new ManagedServiceThread( bundleContext, pid );
try
{
// start threads -- both are waiting to be triggered
ct.start();
mt.start();
// trigger for action
ct.trigger();
mt.trigger();
// wait for threads to terminate
ct.join();
mt.join();
// wait for all tasks to terminate
delay();
final boolean isConfigured = mt.isConfigured();
final ArrayList<Dictionary> configs = mt.getConfigs();
// terminate mt to ensure no further config updates
mt.cleanup();
TestCase.assertTrue( "Last update call must have been with configuration", isConfigured);
if ( configs.size() == 0 )
{
TestCase.fail( "No configuration provided to ManagedService at all" );
}
else if ( configs.size() == 2 )
{
final Dictionary props0 = configs.get( 0 );
final Dictionary props1 = configs.get( 1 );
TestCase.assertNull( "Expected first (of two) updates without configuration", props0 );
TestCase.assertNotNull( "Expected second (of two) updates with configuration", props1 );
}
else if ( configs.size() == 1 )
{
final Dictionary props = configs.get( 0 );
TestCase.assertNotNull( "Expected non-null configuration: " + props, props );
}
else
{
TestCase.fail( "Unexpectedly got " + configs.size() + " updated" );
}
}
finally
{
mt.cleanup();
ct.cleanup();
}
}
// runs a single test to encounter the race condition between
// ManagedServiceFactory registration and Configuration.update(Dictionary)
// This test creates/updates configuration and registers a
// ManagedServiceFactory almost at the same time. The ManagedServiceFactory
// must receive the configuration properties exactly once.
private void single_test_ManagedServiceFactory_race_condition_test( final int counter ) throws IOException,
InterruptedException
{
final String factoryPid = "single_test_ManagedServiceFactory_race_condition_test." + counter;
final ConfigureThread ct = new ConfigureThread( getConfigurationAdmin(), factoryPid, true );
final ManagedServiceFactoryThread mt = new ManagedServiceFactoryThread( bundleContext, factoryPid );
try
{
// start threads -- both are waiting to be triggered
ct.start();
mt.start();
// trigger for action
ct.trigger();
mt.trigger();
// wait for threads to terminate
ct.join();
mt.join();
// wait for all tasks to terminate
delay();
final boolean isConfigured = mt.isConfigured();
final ArrayList<Dictionary> configs = mt.getConfigs();
// terminate mt to ensure no further config updates
mt.cleanup();
TestCase.assertTrue( "Last update call must have been with configuration", isConfigured);
if ( configs.size() == 0 )
{
TestCase.fail( "No configuration provided to ManagedServiceFactory at all" );
}
else if ( configs.size() == 1 )
{
final Dictionary props = configs.get( 0 );
TestCase.assertNotNull( "Expected non-null configuration: " + props, props );
}
else
{
TestCase.fail( "Unexpectedly got " + configs.size() + " updated" );
}
}
finally
{
mt.cleanup();
ct.cleanup();
}
}
}