de.dentrassi.pm.aspect.upgrade.UpgradeTaskProvider.java Source code

Java tutorial

Introduction

Here is the source code for de.dentrassi.pm.aspect.upgrade.UpgradeTaskProvider.java

Source

/*******************************************************************************
 * Copyright (c) 2015 IBH SYSTEMS GmbH.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBH SYSTEMS GmbH - initial API and implementation
 *******************************************************************************/
package de.dentrassi.pm.aspect.upgrade;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;

import de.dentrassi.osgi.profiler.Profile;
import de.dentrassi.osgi.profiler.Profile.Handle;
import de.dentrassi.osgi.web.LinkTarget;
import de.dentrassi.osgi.web.RequestMethod;
import de.dentrassi.pm.aspect.ChannelAspectFactory;
import de.dentrassi.pm.aspect.ChannelAspectProcessor;
import de.dentrassi.pm.common.ChannelAspectInformation;
import de.dentrassi.pm.common.Version;
import de.dentrassi.pm.common.web.Button;
import de.dentrassi.pm.common.web.Modifier;
import de.dentrassi.pm.storage.Channel;
import de.dentrassi.pm.storage.service.StorageService;
import de.dentrassi.pm.todo.BasicTask;
import de.dentrassi.pm.todo.DefaultTaskProvider;
import de.dentrassi.pm.todo.Task;

public class UpgradeTaskProvider extends DefaultTaskProvider implements EventHandler {
    private final static Logger logger = LoggerFactory.getLogger(UpgradeTaskProvider.class);

    private static final Button PERFORM_BUTTON = new Button("Refresh aspect", "refresh", Modifier.DEFAULT);

    private static final Button PERFORM_ALL_BUTTON = new Button("Refresh channel", "refresh", Modifier.DEFAULT);

    private static final Button PERFORM_ALL_SUPER_BUTTON = new Button("Refresh all channels", "refresh",
            Modifier.DEFAULT);

    private final ServiceListener listener = new ServiceListener() {

        @Override
        public void serviceChanged(final ServiceEvent event) {
            handleServiceChange(event);
        }
    };

    private StorageService service;

    private BundleContext context;

    public UpgradeTaskProvider() {
    }

    public void setService(final StorageService service) {
        this.service = service;
    }

    public void start() throws Exception {
        logger.info("Starting channel aspect upgrade watcher");

        this.context = FrameworkUtil.getBundle(UpgradeTaskProvider.class).getBundleContext();
        this.context.addServiceListener(this.listener,
                String.format("(%s=%s)", Constants.OBJECTCLASS, ChannelAspectFactory.class.getName()));

        // fork off the initial scan, since the component has to start even if the database is not present

        final Thread t = new Thread() {
            @Override
            public void run() {
                refresh();
            }
        };
        t.setName("UpgradeTaskProvider/initialScan");
        t.setDaemon(true);
        t.start();
    }

    public void stop() {
        this.context.removeServiceListener(this.listener);
    }

    protected void handleServiceChange(final ServiceEvent event) {
        logger.debug("service change - {} - {}", event.getType(), event.getServiceReference());

        switch (event.getType()) {
        case ServiceEvent.UNREGISTERING:
        case ServiceEvent.REGISTERED:
            refresh();
            break;
        }
    }

    public void refresh() {
        logger.info("Refreshing");
        setTasks(updateState());
    }

    private List<Task> updateState() {
        try (Handle handle = Profile.start(this, "updateState")) {
            final Map<String, ChannelAspectInformation> infos = ChannelAspectProcessor
                    .scanAspectInformations(this.context);

            final List<Task> result = new LinkedList<>();

            final Multimap<String, Channel> missing = HashMultimap.create();

            final Multimap<Channel, String> channels = HashMultimap.create();

            for (final Channel channel : this.service.listChannels()) {
                logger.debug("Checking channel: {}", channel.getId());

                final Map<String, String> states = channel.getAspectStates();
                for (final Map.Entry<String, String> entry : states.entrySet()) {
                    logger.debug("\t{}", entry.getKey());

                    final ChannelAspectInformation info = infos.get(entry.getKey());
                    if (info == null) {
                        missing.put(entry.getKey(), channel);
                    } else {
                        logger.debug("\t{} - {} -> {}", info.getFactoryId(), entry.getValue(), info.getVersion());

                        if (!info.getVersion().equals(Version.valueOf(entry.getValue()))) {
                            result.add(makeUpgradeTask(channel, info, entry.getValue()));
                            channels.put(channel, entry.getKey());
                        }
                    }
                }
            }

            for (final Map.Entry<Channel, Collection<String>> entry : channels.asMap().entrySet()) {
                final Channel channel = entry.getKey();
                final LinkTarget target = new LinkTarget(
                        String.format("/channel/%s/refreshAllAspects", channel.getId()));
                final String description = "Channel aspects active in this channel have been updated. You can refresh the whole channel.";
                result.add(new BasicTask("Refresh channel: " + makeChannelTitle(channel), 100, description, target,
                        RequestMethod.GET, PERFORM_ALL_BUTTON));
            }

            for (final Map.Entry<String, Collection<Channel>> entry : missing.asMap().entrySet()) {
                final String missingChannels = entry.getValue().stream().map(Channel::getId)
                        .collect(Collectors.joining(", "));
                result.add(new BasicTask(String.format("Fix missing channel aspect: %s", entry.getKey()), 1,
                        String.format(
                                "The channel aspect '%s' is being used but not installed in the system. Channels: %s",
                                entry.getKey(), missingChannels),
                        null));
            }

            if (!channels.isEmpty()) {
                result.add(new BasicTask("Refresh all channels", 1, "Refresh all channels in one big task",
                        new LinkTarget(String.format("/job/%s/create", UpgradeAllChannelsJob.ID)),
                        RequestMethod.POST, PERFORM_ALL_SUPER_BUTTON));
            }

            return result;
        }
    }

    private String makeChannelTitle(final Channel channel) {
        if (channel.getName() != null) {
            return String.format("%s (%s)", channel.getName(), channel.getId());
        } else {
            return channel.getId();
        }
    }

    private Task makeUpgradeTask(final Channel channel, final ChannelAspectInformation info,
            final String fromVersion) {
        final String channelName = makeChannelTitle(channel);

        String factoryId;
        try {
            factoryId = URLEncoder.encode(info.getFactoryId(), "UTF-8");
        } catch (final UnsupportedEncodingException e) {
            factoryId = info.getFactoryId();
        }

        final LinkTarget target = new LinkTarget(
                String.format("/channel/%s/refreshAspect?aspect=%s", channel.getId(), factoryId));

        final String description = String.format(
                "The aspect %s (%s) in channel %s was upgraded from version %s to %s. The channel aspect has to be re-processed.",
                info.getLabel(), info.getFactoryId(), channelName, fromVersion, info.getVersion());
        return new BasicTask("Upgrade aspect data", 1_000, description, target, RequestMethod.POST, PERFORM_BUTTON);
    }

    @Override
    public void handleEvent(final Event event) {
        logger.debug("Received event - {}", event.getTopic());

        final String topic = event.getTopic();
        final Object op = event.getProperty("operation");

        if (topic.startsWith("drone/channel/")) {
            if ("remove".equals(op) || "refresh".equals(op)) {
                refresh();
            }
        }
        if (topic.endsWith("drone/database/schemaUpgrade")) {
            refresh();
        }
    }
}