blob: 6de36eea56b3bcb0346c870f6ea887723fdf0ebe [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.scr.integration.components.felix3680_2;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.felix.scr.impl.manager.ThreadDump;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.runtime.ServiceComponentRuntime;
import org.osgi.service.component.runtime.dto.ComponentDescriptionDTO;
import org.osgi.service.log.LogService;
public class Main implements Runnable
{
private static final int LATCH_TIMEOUT = 10000;
private volatile ComponentContext m_ctx;
private volatile AtomicInteger m_counter = new AtomicInteger();
private volatile CountDownLatch m_enabledLatch;
private volatile CountDownLatch m_disabledLatch;
private volatile LogService m_logService;
private ServiceComponentRuntime m_scr;
private final Executor m_exec = Executors.newFixedThreadPool( 12 );
private volatile BundleContext m_bctx;
volatile ConcurrentHashMap<Class, ServiceRegistration> m_registrations = new ConcurrentHashMap<Class, ServiceRegistration>();
volatile Exception _bindStackTrace;
private volatile boolean running = true;
/**
* Helper used to randomly enable or disable a list of components.
*/
class RegistrationHelper
{
public void registerBCDEFGHIJK( Executor exec )
{
enableOrDisable( true );
}
public void unregisterBCDEFGHIJK( Executor exec )
{
enableOrDisable( false );
}
private void enableOrDisable( final boolean enable )
{
if ( enable )
{
register( B.class );
register( C.class );
register( D.class );
register( E.class );
register( F.class );
register( G.class );
register( H.class );
register( I.class );
register( J.class );
register( K.class );
}
else
{
unregister( B.class );
unregister( C.class );
unregister( D.class );
unregister( E.class );
unregister( F.class );
unregister( G.class );
unregister( H.class );
unregister( I.class );
unregister( J.class );
unregister( K.class );
}
}
private void register( final Class clazz )
{
m_exec.execute( new Runnable()
{
public void run()
{
try
{
Object instance = clazz.newInstance();
m_registrations.put( clazz, m_bctx.registerService( clazz.getName(), instance, null ) );
m_enabledLatch.countDown();
}
catch ( Throwable e )
{
m_logService.log( LogService.LOG_ERROR, "error while enabling " + clazz, e );
}
}
} );
}
private void unregister( final Class clazz )
{
m_exec.execute( new Runnable()
{
public void run()
{
try
{
ServiceRegistration sr = m_registrations.remove( clazz );
sr.unregister();
m_disabledLatch.countDown();
}
catch ( Throwable e )
{
m_logService.log( LogService.LOG_ERROR, "error while enabling " + clazz, e );
}
}
} );
}
}
void bindSCR( ServiceComponentRuntime scr )
{
m_scr = scr;
}
void bindLogService( LogService logService )
{
m_logService = logService;
}
void bindA( ServiceReference sr )
{
Exception trace = new Exception( "bindA (" + Thread.currentThread() + ")" );
if ( _bindStackTrace != null )
{
m_logService.log( LogService.LOG_ERROR, "Already bound A from stacktrace:", _bindStackTrace );
m_logService.log( LogService.LOG_ERROR, "Current stacktrace is:", trace );
return;
}
_bindStackTrace = trace;
A a = ( A ) m_ctx.locateService( "a", sr );
if ( a == null )
{
throw new IllegalStateException( "bindA: bundleContext.getService returned null" );
}
if ( m_counter.incrementAndGet() != 1 )
{
throw new IllegalStateException( "bindA: invalid counter value: " + m_counter );
}
m_enabledLatch.countDown();
}
void unbindA( A a )
{
if ( m_counter.decrementAndGet() != 0 )
{
throw new IllegalStateException( "unbindA: invalid counter value: " + m_counter );
}
_bindStackTrace = null;
m_disabledLatch.countDown();
}
void start( ComponentContext ctx )
{
m_ctx = ctx;
m_bctx = ctx.getBundleContext();
m_ctx.getBundleContext().registerService( Executor.class.getName(), m_exec, null );
new Thread( this ).start();
}
void stop() throws InterruptedException
{
synchronized ( this )
{
running = false;
}
if (m_enabledLatch != null)
{
m_enabledLatch.await( 1, TimeUnit.MILLISECONDS );
}
if (m_disabledLatch != null)
{
m_disabledLatch.await( 1, TimeUnit.MILLISECONDS );
}
}
public void run()
{
int loop = 0;
while ( iterate() )
{
RegistrationHelper registry = new RegistrationHelper();
registry.registerBCDEFGHIJK( m_exec );
try
{
if ( !m_enabledLatch.await( LATCH_TIMEOUT, TimeUnit.MILLISECONDS ) )
{
System.out.println( "Did not get A injected timely ... see logs.txt" );
m_logService.log( LogService.LOG_ERROR, "enableLatch TIMEOUT" );
m_logService.log(LogService.LOG_ERROR, dumpThreads());
dumpA();
System.exit( 1 );
}
}
catch ( InterruptedException e )
{
}
registry.unregisterBCDEFGHIJK( m_exec );
try
{
if ( !m_disabledLatch.await( LATCH_TIMEOUT, TimeUnit.MILLISECONDS ) )
{
System.out.println( "Could not disable components timely ... see logs.txt" );
m_logService.log( LogService.LOG_ERROR, "disableLatch TIMEOUT" );
m_logService.log(LogService.LOG_ERROR, dumpThreads());
dumpA();
System.exit( 1 );
}
}
catch ( InterruptedException e )
{
}
++loop;
if ( loop % 100 == 0 )
{
m_logService.log( LogService.LOG_INFO, "Performed " + loop + " tests." );
}
}
}
private synchronized boolean iterate()
{
if ( running )
{
m_enabledLatch = new CountDownLatch( 11 ); // 10 for registrations of B,C,D,E,F,G,H,I,J,K + 1 for Main.bindA
m_disabledLatch = new CountDownLatch( 11 ); // 10 for unregistrations of B,C,D,E,F,G,H,I,J,K + 1 for Main.unbindA
}
return running;
}
private String dumpThreads()
{
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
StringBuffer b = new StringBuffer( "Thread dump\n" );
ThreadInfo[] infos = threadMXBean.dumpAllThreads( threadMXBean.isObjectMonitorUsageSupported(), threadMXBean.isSynchronizerUsageSupported() );
for ( int i = 0; i < infos.length; i++ )
{
ThreadInfo ti = infos[i];
b.append( "\n\nThreadId: " ).append( ti.getThreadId() ).append( " : name: " ).append( ti.getThreadName() ).append( " State: " ).append( ti.getThreadState() );
b.append( "\n LockInfo: " ).append( ti.getLockInfo() ).append( " LockOwnerId: " ).append( ti.getLockOwnerId() ).append( " LockOwnerName: ").append( ti.getLockOwnerName() );
StackTraceElement[] stackTrace = ti.getStackTrace();
for (int j = 0; j < stackTrace.length; j++ )
{
b.append( "\n " ).append( stackTrace[j] );
}
}
return b.toString();
}
private void dumpA()
{
ComponentDescriptionDTO c = m_scr
.getComponentDescriptionDTO(m_bctx.getBundle(), "org.apache.felix.scr.integration.components.felix3680_2.A" );
m_logService.log( LogService.LOG_WARNING, "State of " + c.name + " enabled:" + m_scr.isComponentEnabled(c) + "\n" );
}
}