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

org.killbill.billing.osgi.api.DefaultPluginsInfoApi Maven / Gradle / Ivy

/*
 * Copyright 2015 Groupon, Inc
 * Copyright 2015 The Billing Project, LLC
 *
 * The Billing Project 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.killbill.billing.osgi.api;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import javax.annotation.Nullable;

import org.killbill.billing.osgi.BundleRegistry;
import org.killbill.billing.osgi.BundleRegistry.BundleWithMetadata;
import org.killbill.billing.osgi.api.config.PluginConfig;
import org.killbill.billing.osgi.api.config.PluginLanguage;
import org.killbill.billing.osgi.pluginconf.PluginConfigException;
import org.killbill.billing.osgi.pluginconf.PluginFinder;
import org.killbill.billing.util.nodes.KillbillNodesApi;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Ordering;
import com.google.inject.Inject;

public class DefaultPluginsInfoApi implements PluginsInfoApi {

    private static final Logger logger = LoggerFactory.getLogger(DefaultPluginsInfoApi.class);

    private static final Ordering PLUGIN_INFO_ORDERING =  Ordering.natural().onResultOf(new Function() {
        @Override
        public String apply(final PluginInfo input) {
            return toPluginFullName(input.getPluginName(), input.getVersion());
        }

        private String toPluginFullName(final String pluginName, final String pluginVersion) {
            final StringBuilder tmp = new StringBuilder(pluginName);
            if (pluginVersion != null) {
                tmp.append(pluginVersion);
            }
            return tmp.toString();
        }
    });

    private final BundleRegistry bundleRegistry;
    private final PluginFinder pluginFinder;
    private final KillbillNodesApi nodesApi;

    @Inject
    public DefaultPluginsInfoApi(final BundleRegistry bundleRegistry, final PluginFinder pluginFinder, final KillbillNodesApiHolder nodesApiHolder) {
        this.bundleRegistry = bundleRegistry;
        this.pluginFinder = pluginFinder;
        this.nodesApi = nodesApiHolder.getNodesApi();
    }

    @Override
    public Iterable getPluginsInfo() {

        final List result = new ArrayList();
        for (final String pluginName : pluginFinder.getAllPlugins().keySet()) {

            final BundleWithMetadata installedBundleOrNull = bundleRegistry.getBundle(pluginName);

            final LinkedList pluginVersions = pluginFinder.getAllPlugins().get(pluginName);
            boolean isSelectedForStart = true; // The first one in the list is the one selected for start
            for (final PluginConfig curVersion : pluginVersions) {
                final PluginInfo pluginInfo;
                if (installedBundleOrNull != null && curVersion.getVersion().equals(installedBundleOrNull.getVersion())) {
                    pluginInfo = new DefaultPluginInfo(curVersion.getPluginKey(),
                                                       installedBundleOrNull.getBundle().getSymbolicName(),
                                                       installedBundleOrNull.getPluginName(),
                                                       installedBundleOrNull.getVersion(),
                                                       toPluginState(installedBundleOrNull),
                                                       isSelectedForStart,
                                                       installedBundleOrNull.getServiceNames());
                } else {
                    pluginInfo = new DefaultPluginInfo(curVersion.getPluginKey(), null, curVersion.getPluginName(), curVersion.getVersion(), toPluginState(null), isSelectedForStart, ImmutableSet.of());
                }
                isSelectedForStart = false;
                result.add(pluginInfo);
            }
        }
        for (final BundleWithMetadata osgiBundle : bundleRegistry.getPureOSGIBundles()) {
            if (osgiBundle.getBundle().getSymbolicName() != null) {
                final PluginInfo pluginInfo = new DefaultPluginInfo(null, osgiBundle.getBundle().getSymbolicName(), osgiBundle.getPluginName(), osgiBundle.getVersion(), toPluginState(osgiBundle), true, ImmutableSet.of());
                result.add(pluginInfo);
            }
        }

        return PLUGIN_INFO_ORDERING.sortedCopy(result);
    }

    @Override
    public void notifyOfStateChanged(final PluginStateChange newState, final String pluginKey, final String pluginName, final String pluginVersion, final PluginLanguage pluginLanguage) {
        try {

            // Refresh our filesystem view so it shows up/disappears in the list of installed plugin
            pluginFinder.reloadPlugins();

            // The KPM plugin will actually always pass both so the pluginKey will never be used, but it is good to keep the code generic
            final String resolvedPluginName = pluginName != null ?
                                              pluginName :
                                              (pluginFinder.resolvePluginKey(pluginKey) != null ? pluginFinder.resolvePluginKey(pluginKey).getPluginName() : null);

            final String defaultPluginVersion = pluginFinder.getPluginVersionSelectedForStart(resolvedPluginName);
            final boolean isSelectedForStart = defaultPluginVersion != null && defaultPluginVersion.equals(pluginVersion);
            switch (newState) {
                case NEW_VERSION:
                    // Nothing special to do; we don't try to OSGI 'install' the plugin at this time, this will be done
                    // when we start it
                    break;

                case DISABLED:
                    // If plugin is in the bundleRegistry, we remove it (stopping it if required)
                    bundleRegistry.stopAndUninstallNewBundle(resolvedPluginName, pluginVersion);
                    break;

                default:
                    throw new IllegalStateException("Invalid PluginStateChange " + newState);
            }

            // Notify KillbillNodesService to update the node_infos table
            if (nodesApi != null) {
                final PluginInfo pluginInfo = new DefaultPluginInfo(pluginKey, null, resolvedPluginName, pluginVersion, toPluginState(null), isSelectedForStart, ImmutableSet.of());
                nodesApi.notifyPluginChanged(pluginInfo, getPluginsInfo());
            }
        } catch (final PluginConfigException e) {
            logger.error("Failed to handle notifyOfStateChanged: ", e);
        } catch (final IOException e) {
            logger.error("Failed to handle notifyOfStateChanged: ", e);
        } catch (final BundleException e) {
            logger.error("Failed to handle notifyOfStateChanged: ", e);
        }
    }

    public static PluginState toPluginState(@Nullable final BundleWithMetadata bundle) {
        return (bundle != null && bundle.getBundle().getState() == Bundle.ACTIVE) ? PluginState.RUNNING : PluginState.STOPPED;
    }

    public static final class DefaultPluginInfo implements PluginInfo {

        private final String pluginKey;
        private final String pluginName;
        private final String pluginSymbolicName;
        private final String version;
        private final Set services;
        private final PluginState state;
        private final boolean isSelectedForStart;

        public DefaultPluginInfo(final String pluginKey, final String pluginSymbolicName, final String pluginName, final String version, final PluginState state, final boolean isSelectedForStart, final Set services) {
            this.pluginKey = pluginKey;
            this.pluginSymbolicName = pluginSymbolicName;
            this.pluginName = pluginName;
            this.version = version;
            this.state = state;
            this.isSelectedForStart = isSelectedForStart;
            this.services = services;
        }

        @Override
        public String getPluginKey() {
            return pluginKey;
        }

        @Override
        public String getBundleSymbolicName() {
            return pluginSymbolicName;
        }

        @Override
        public String getPluginName() {
            return pluginName;
        }

        @Override
        public String getVersion() {
            return version;
        }

        @Override
        public Set getServices() {
            return services;
        }

        @Override
        public boolean isSelectedForStart() {
            return isSelectedForStart;
        }

        @Override
        public PluginState getPluginState() {
            return state;
        }
    }

    public static class DefaultPluginServiceInfo implements PluginServiceInfo {

        private final String serviceTypeName;
        private final String registrationName;

        public DefaultPluginServiceInfo(final String serviceTypeName, final String registrationName) {
            this.serviceTypeName = serviceTypeName;
            this.registrationName = registrationName;
        }

        @Override
        public String getServiceTypeName() {
            return serviceTypeName;
        }

        @Override
        public String getRegistrationName() {
            return registrationName;
        }

        @Override
        public boolean equals(final Object o) {
            if (this == o) {
                return true;
            }
            if (!(o instanceof DefaultPluginServiceInfo)) {
                return false;
            }

            final DefaultPluginServiceInfo that = (DefaultPluginServiceInfo) o;

            if (serviceTypeName != null ? !serviceTypeName.equals(that.serviceTypeName) : that.serviceTypeName != null) {
                return false;
            }
            return !(registrationName != null ? !registrationName.equals(that.registrationName) : that.registrationName != null);

        }

        @Override
        public int hashCode() {
            int result = serviceTypeName != null ? serviceTypeName.hashCode() : 0;
            result = 31 * result + (registrationName != null ? registrationName.hashCode() : 0);
            return result;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy