All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ch.sourcepond.commons.smartswitch.impl.DefaultInvocationHandler Maven / Gradle / Ivy

/*Copyright (C) 2017 Roland Hauser, 
Licensed 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 ch.sourcepond.commons.smartswitch.impl;

import org.osgi.framework.Constants;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * Implementation of the SmartSwitch logic. This class actually switches between a default service and an OSGi service
 * when it becomes available.
 */
class DefaultInvocationHandler implements InvocationHandler, ServiceListener {
    private final AtomicReference reference = new AtomicReference<>();
    private final Supplier supplier;
    private final Consumer serviceAvailableHook;
    private ServiceReference currentServiceReference;

    DefaultInvocationHandler(final Supplier pSupplier, final Consumer pServiceAvailableHook) {
        assert pSupplier != null : "pSupplier cannot be null";
        assert pServiceAvailableHook != null : "pServiceAvailableHook cannot be null";
        supplier = pSupplier;
        serviceAvailableHook = pServiceAvailableHook;
    }

    @SuppressWarnings("unchecked")
    private T getService(final ServiceReference pRef) {
        return (T) pRef.getBundle().getBundleContext().getService(pRef);
    }

    synchronized void initService(final ServiceReference pRefOrNull) {
        if (null != pRefOrNull && null == currentServiceReference) {
            final T serviceOrNull = getService(pRefOrNull);
            if (serviceOrNull != null) {
                currentServiceReference = pRefOrNull;
                reference.set(getService(pRefOrNull));
            }
        }

        if (reference.get() == null) {
            reference.set(supplier.get());
        }
    }

    @Override
    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
        return method.invoke(reference.get(), args);
    }

    private int getCurrentRanking() {
        return currentServiceReference == null ? Integer.MIN_VALUE : getRanking(currentServiceReference);
    }

    private int getRanking(final ServiceReference pRef) {
        final Object ranking = pRef.getProperty(Constants.SERVICE_RANKING);
        if (null == ranking || !(ranking instanceof Integer)) {
            return 0;
        }
        return (Integer) ranking;
    }

    @Override
    @SuppressWarnings("unchecked")
    public synchronized void serviceChanged(final ServiceEvent event) {
        switch (event.getType()) {
            case ServiceEvent.REGISTERED: {
                switchService(event);
                break;
            }
            case ServiceEvent.MODIFIED_ENDMATCH:
            case ServiceEvent.UNREGISTERING: {
                switchToDefault(event);
                break;
            }
            default: {
                // noop
            }
        }
    }

    private void switchToDefault(final ServiceEvent event) {
        if (event.getServiceReference().equals(currentServiceReference)) {
            reference.set(supplier.get());
            currentServiceReference = null;
        }
    }

    private void switchService(final ServiceEvent event) {
        final ServiceReference serviceReference = event.getServiceReference();
        if (getRanking(serviceReference) > getCurrentRanking()) {
            final T previous = reference.getAndSet(getService(serviceReference));
            assert previous != null : "previous cannot be null";

            // If the previously held service was a default service.
            if (currentServiceReference == null) {
                serviceAvailableHook.accept(previous);
            }

            // Remember current reference
            currentServiceReference = serviceReference;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy