| /* |
| * 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; |
| } |
| |
| } |