blob: fa0616fae46ab3b9e9e3a9c975c4f202fcde073d [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.impl.manager;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.osgi.service.log.LogService;
abstract class RegistrationManager<T>
{
enum RegState {unregistered, registered};
private static class RegStateWrapper
{
private final CountDownLatch latch = new CountDownLatch(1);
private final RegState regState;
RegStateWrapper( RegState regState )
{
this.regState = regState;
}
public RegState getRegState()
{
return regState;
}
public CountDownLatch getLatch()
{
return latch;
}
@Override
public int hashCode()
{
return regState.hashCode();
}
@Override
public boolean equals(Object other)
{
return other instanceof RegStateWrapper && regState == ((RegStateWrapper)other).getRegState();
}
@Override
public String toString()
{
return regState.toString();
}
}
private final Lock registrationLock = new ReentrantLock();
//Deque, ArrayDeque if we had java 6
private final List<RegStateWrapper> opqueue = new ArrayList<>();
private volatile T m_serviceRegistration;
/**
*
* @param desired desired registration state
* @param services services to register this under
* @return true if this request results in a state change, false if we are already in the desired state or some other thread
* will deal with the consequences of the state change.
*/
boolean changeRegistration( RegState desired, String[] services )
{
RegStateWrapper rsw = null;
registrationLock.lock();
try
{
if (opqueue.isEmpty())
{
if ((desired == RegState.unregistered) == (m_serviceRegistration == null))
{
log( LogService.LOG_DEBUG, "Already in desired state {0}", null, desired);
return false;
}
}
else if (opqueue.get( opqueue.size() - 1 ).getRegState() == desired)
{
log( LogService.LOG_DEBUG, "Duplicate request on other thread: registration change queue {0}", null, opqueue);
rsw = opqueue.get( opqueue.size() - 1 );
return false; //another thread will do our work and owns the state change
}
rsw = new RegStateWrapper( desired );
opqueue.add( rsw );
if (opqueue.size() > 1)
{
log( LogService.LOG_DEBUG, "Allowing other thread to process request: registration change queue {0}", null, opqueue);
return true; //some other thread will do it later but this thread owns the state change.
}
//we're next
do
{
log( LogService.LOG_DEBUG, "registration change queue {0}", null, opqueue);;
RegStateWrapper next = opqueue.get( 0 );
T serviceRegistration = m_serviceRegistration;
if ( next.getRegState() == RegState.unregistered)
{
m_serviceRegistration = null;
}
registrationLock.unlock();
try
{
if (next.getRegState() == RegState.registered)
{
serviceRegistration = register(services );
}
else
{
if ( serviceRegistration != null )
{
unregister( serviceRegistration );
}
else
{
log( LogService.LOG_ERROR, "Unexpected unregistration request with no registration present", new Exception("Stack trace"));
}
}
}
finally
{
registrationLock.lock();
opqueue.remove(0);
if ( next.getRegState() == RegState.registered)
{
m_serviceRegistration = serviceRegistration;
postRegister( m_serviceRegistration );
}
next.getLatch().countDown();
}
}
while (!opqueue.isEmpty());
return true;
}
finally
{
registrationLock.unlock();
if (rsw != null)
{
try
{
if ( !rsw.getLatch().await( getTimeout(), TimeUnit.MILLISECONDS ))
{
log( LogService.LOG_ERROR, "Timeout waiting for reg change to complete {0}", null, rsw.getRegState());
reportTimeout();
}
}
catch ( InterruptedException e )
{
try
{
if ( !rsw.getLatch().await( getTimeout(), TimeUnit.MILLISECONDS ))
{
log( LogService.LOG_ERROR, "Timeout waiting for reg change to complete {0}", null, rsw.getRegState());
reportTimeout();
}
}
catch ( InterruptedException e1 )
{
log( LogService.LOG_ERROR, "Interrupted twice waiting for reg change to complete {0}",null, rsw.getRegState());
}
Thread.currentThread().interrupt();
}
}
}
}
abstract T register(String[] services);
abstract void postRegister(T t);
abstract void unregister(T serviceRegistration);
abstract void log( int level, String message, Throwable ex, Object... arguments );
abstract long getTimeout();
abstract void reportTimeout();
T getServiceRegistration()
{
return m_serviceRegistration;
}
}