io.joynr.channel.ChannelUrlDirectoyImpl.java Source code

Java tutorial

Introduction

Here is the source code for io.joynr.channel.ChannelUrlDirectoyImpl.java

Source

package io.joynr.channel;

/*
 * #%L
 * %%
 * Copyright (C) 2011 - 2014 BMW Car IT GmbH
 * %%
 * 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.
 * #L%
 */

import io.joynr.provider.DeferredVoid;
import io.joynr.provider.Promise;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import joynr.infrastructure.ChannelUrlDirectoryAbstractProvider;
import joynr.types.ChannelUrlInformation;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;

/**
 * The channelurldirectory stores channelIds mapped to channelUrls.
 *
 *
 * channelurls are stored in a concurrentHashMap. Using a in memory database could be possible optimization.
 */
// TODO Evaluate pro /cons of a in memory database

@Singleton
public class ChannelUrlDirectoyImpl extends ChannelUrlDirectoryAbstractProvider {
    private static final Logger logger = LoggerFactory.getLogger(ChannelUrlDirectoyImpl.class);

    public static final String CHANNELURL_INACTIVE_TIME_IN_MS = "joynr.channel.channelurlinactivetime";

    final long channelurInactiveTimeInMS;

    private ConcurrentHashMap<String, ChannelUrlInformation> registeredChannels = new ConcurrentHashMap<String, ChannelUrlInformation>();

    Map<String, Long> inactiveChannelIds = new ConcurrentHashMap<String, Long>();

    private Thread cleanupThread;

    private Map<String, List<GetUrlsForChannelDeferred>> pendingDeferredsMap;

    ConcurrentHashMap<String, ChannelUrlInformation> getRegisteredChannels() {
        return registeredChannels;
    }

    @Inject
    public ChannelUrlDirectoyImpl(@Named(CHANNELURL_INACTIVE_TIME_IN_MS) long inactiveTimeInMS) {
        channelurInactiveTimeInMS = inactiveTimeInMS;
        pendingDeferredsMap = Maps.newConcurrentMap();
        cleanupThread = new Thread(new Runnable() {
            @Override
            public void run() {
                cleanupRunnable();
            }
        });
        cleanupThread.start();
    }

    private void cleanupRunnable() {
        while (true) {
            synchronized (cleanupThread) {
                if (inactiveChannelIds.size() == 0) {
                    try {
                        cleanupThread.wait();
                    } catch (InterruptedException e) {
                    }
                } else {
                    long currentTime = System.currentTimeMillis();
                    long timeToSleep = -1;
                    for (Entry<String, Long> inactiveChannelId : inactiveChannelIds.entrySet()) {
                        String channelId = inactiveChannelId.getKey();
                        long passedTime = currentTime - inactiveChannelId.getValue();
                        if (passedTime >= channelurInactiveTimeInMS) {
                            logger.debug("GLOBAL unregisterChannelUrls channelId: {}", channelId);
                            registeredChannels.remove(channelId);
                            inactiveChannelIds.remove(channelId);
                        } else {
                            if (timeToSleep == -1 || timeToSleep > channelurInactiveTimeInMS - passedTime) {
                                timeToSleep = channelurInactiveTimeInMS - passedTime;
                            }
                        }
                    }
                    if (timeToSleep != -1) {
                        try {
                            cleanupThread.wait(timeToSleep);
                        } catch (InterruptedException e) {
                        }
                    }
                }
            }
        }
    }

    @Override
    public Promise<GetUrlsForChannelDeferred> getUrlsForChannel(String channelId) {
        GetUrlsForChannelDeferred deferred = new GetUrlsForChannelDeferred();

        ChannelUrlInformation channelUrlInformation = registeredChannels.get(channelId);
        if (channelUrlInformation == null) {
            addPendingCallback(deferred, channelId);
            logger.warn(
                    "GLOBAL getUrlsForChannel for Channel: {} found nothing. Invoke callbacks once ChannelUrlInformation becomes available",
                    channelId);
        } else {
            logger.debug("GLOBAL getUrlsForChannel ChannelUrls for channelId {} found: {}", channelId,
                    channelUrlInformation);
            deferred.resolve(channelUrlInformation);
        }
        return new Promise<GetUrlsForChannelDeferred>(deferred);
    }

    private synchronized void addPendingCallback(GetUrlsForChannelDeferred deferred, String channelId) {
        if (pendingDeferredsMap.get(channelId) == null) {
            pendingDeferredsMap.put(channelId, Lists.<GetUrlsForChannelDeferred>newArrayList());
        }
        pendingDeferredsMap.get(channelId).add(deferred);
        // TODO drop the newly added callback from the pendingCallbackMap after a while, avoiding a continuously growing
        // map
    }

    @Override
    public Promise<DeferredVoid> registerChannelUrls(String channelId,
            ChannelUrlInformation channelUrlInformation) {
        logger.debug("GLOBAL registerChannelUrls channelId: {} channelUrls: {}", channelId, channelUrlInformation);
        DeferredVoid deferred = new DeferredVoid();
        registeredChannels.put(channelId, channelUrlInformation);
        inactiveChannelIds.remove(channelId);
        if (deferred != null) {
            deferred.resolve();
        }
        List<GetUrlsForChannelDeferred> pendingDeferreds = pendingDeferredsMap.remove(channelId);
        if (pendingDeferreds != null) {
            for (GetUrlsForChannelDeferred pendingDeferred : pendingDeferreds) {
                pendingDeferred.resolve(channelUrlInformation);
            }
        }
        return new Promise<DeferredVoid>(deferred);
    }

    @Override
    public Promise<DeferredVoid> unregisterChannelUrls(String channelId) {
        DeferredVoid deferred = new DeferredVoid();
        inactiveChannelIds.put(channelId, System.currentTimeMillis());
        synchronized (cleanupThread) {
            cleanupThread.notify();
        }

        deferred.resolve();
        return new Promise<DeferredVoid>(deferred);
    }
}