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

io.relayr.java.storage.DeviceModelCache Maven / Gradle / Ivy

package io.relayr.java.storage;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import javax.inject.Inject;
import javax.inject.Singleton;

import io.relayr.java.RelayrJavaApp;
import io.relayr.java.api.DeviceModelsApi;
import io.relayr.java.api.UserApi;
import io.relayr.java.model.Device;
import io.relayr.java.model.User;
import io.relayr.java.model.models.DeviceModel;
import io.relayr.java.model.models.DeviceModels;
import io.relayr.java.model.models.error.DeviceModelsCacheException;
import io.relayr.java.model.models.error.DeviceModelsException;
import rx.Observable;
import rx.Observer;
import rx.Subscriber;
import rx.functions.Func1;

/**
 * Caches all {@link DeviceModel} objects. Works only if there is Internet connection.
 * Use {@link DeviceModelCache} to determine appropriate model for your device.
 * Example: call {@link DeviceModelCache#getModelById(String)} using modelId from {@link Device#getModelId()}
 */
@Singleton
public class DeviceModelCache {

    private final int LIMIT = 25;
    private final int DEFAULT_TIMEOUT = 7;

    private static final Map sDeviceModels = new ConcurrentHashMap<>();
    private static volatile boolean refreshing = false;
    private static String userId;

    private final DeviceModelsApi modelsApi;
    private final UserApi userApi;

    private int total;
    private int offset;

    @Inject
    public DeviceModelCache(DeviceModelsApi modelsApi, UserApi userApi) {
        this.modelsApi = modelsApi;
        this.userApi = userApi;
        if (RelayrJavaApp.isCachingModels()) refresh();
    }

    /**
     * Returns cache state. Use this method before using {@link #getModelById(String)}
     * @return true if cache is loading and it's not ready for use, false otherwise
     */
    public boolean isLoading() {
        return refreshing;
    }

    /** @return true if models are cached, false otherwise */
    public boolean available() {
        if (!RelayrJavaApp.isCachingModels())
            System.err.println("DeviceModelCache - models are not cached.");
        return RelayrJavaApp.isCachingModels() && !isLoading() && !sDeviceModels.isEmpty();
    }

    /**
     * Returns {@link DeviceModel} depending on specified modelId.
     * Obtain modelId parameter from {@link Device#getModelId()}
     * @param modelId {@link Device#getModelId()}
     * @return {@link DeviceModel} if one is found, null otherwise
     */
    public DeviceModel getModelById(String modelId) throws DeviceModelsException {
        if (!available()) throw DeviceModelsException.cacheNotReady();
        if (modelId == null) throw DeviceModelsException.nullModelId();

        DeviceModel model = sDeviceModels.get(modelId);
        if (model == null) throw DeviceModelsException.deviceModelNotFound();

        return model;
    }

    /**
     * Returns {@link DeviceModel} depending on specified modelId.
     * Search is NOT case sensitive and matches the results with equals.
     * @param name model name
     * @return {@link DeviceModel} if one is found, null otherwise
     */
    public DeviceModel getModelByName(String name) throws DeviceModelsCacheException {
        return getModelByName(name, true, false);
    }

    /**
     * Returns {@link DeviceModel} depending on specified modelId. This search is NOT case sensitive.
     * @param name   model name
     * @param equals if true names will be matched only if they are equal
     * @return {@link DeviceModel} if one is found, null otherwise
     */
    public DeviceModel getModelByName(String name, boolean equals) throws DeviceModelsCacheException {
        return getModelByName(name, equals, false);
    }

    /**
     * Returns {@link DeviceModel} depending on specified modelId.
     * @param name          model name
     * @param equals        if true names will be matched only if they are equal
     * @param caseSensitive true if matching should be case sensitive
     * @return {@link DeviceModel} if one is found, null otherwise
     */
    public DeviceModel getModelByName(String name, boolean equals, boolean caseSensitive) throws DeviceModelsCacheException {
        if (!available()) throw DeviceModelsException.cacheNotReady();
        if (name == null || name.trim().isEmpty()) return null;

        String toFind = caseSensitive ? name : name.toLowerCase();
        if (equals)
            for (DeviceModel deviceModel : sDeviceModels.values()) {
                if (!caseSensitive && deviceModel.getName().toLowerCase().equals(toFind))
                    return deviceModel;
                else if (deviceModel.getName().equals(toFind))
                    return deviceModel;
            }
        else
            for (DeviceModel deviceModel : sDeviceModels.values()) {
                if (!caseSensitive && deviceModel.getName().toLowerCase().contains(toFind))
                    return deviceModel;
                else if (deviceModel.getName().contains(toFind))
                    return deviceModel;
            }

        return null;
    }

    /** @return list of all {@link DeviceModel} supported on Relayr platform ordered by name. */
    public List getAll() throws DeviceModelsCacheException {
        if (!available()) throw DeviceModelsException.cacheNotReady();

        final List models = new ArrayList<>();
        models.addAll(sDeviceModels.values());
        Collections.sort(models, new Comparator() {
            @Override public int compare(DeviceModel o1, DeviceModel o2) {
                return o1.getName().compareToIgnoreCase(o2.getName());
            }
        });
        return models;
    }

    /** Refresh device model cache. */
    public void refresh() {
        if (refreshing) return;
        refreshing = true;
        sDeviceModels.clear();
        RelayrJavaApp.setModelsCache(true);

        modelsApi.getDeviceModels(0, 0)
                .timeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .subscribe(new Subscriber() {
                    @Override public void onCompleted() {}

                    @Override public void onError(Throwable e) {
                        refreshing = false;
                        e.printStackTrace();
                        if (e instanceof TimeoutException) {
                            System.out.println("DeviceModelCache - start loading timeout.");
                            refresh();
                        }
                    }

                    @Override public void onNext(DeviceModels deviceModels) {
                        total = deviceModels.getCount();
                        fetchModels(0);
                    }
                });
    }

    private void fetchModels(final int retry) {
        modelsApi.getDeviceModels(LIMIT, offset)
                .timeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .subscribe(new Observer() {
                    @Override public void onCompleted() {}

                    @Override public void onError(Throwable e) {
                        if (e instanceof TimeoutException && retry <= 3) {
                            fetchModels(retry + 1);
                        } else {
                            if (offset <= total) {
                                offset += LIMIT;
                                fetchModels(0);
                            } else {
                                System.err.println("DeviceModelCache - fetching models failed.");
                                stopRefreshing();
                            }
                        }
                    }

                    @Override public void onNext(DeviceModels models) {
                        if (models == null || models.getModels() == null) return;

                        System.out.println("DeviceModelCache - loaded " +
                                models.getModels().size() + " models.");

                        for (DeviceModel deviceModel : models.getModels())
                            sDeviceModels.put(deviceModel.getId(), deviceModel);

                        offset += LIMIT;
                        if (offset <= total) fetchModels(0);
                        else fetchUserModels(0);
                    }
                });
    }

    private void fetchUserModels(final int retry) {
        userApi.getUserInfo()
                .timeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .flatMap(new Func1>() {
                    @Override public Observable call(User user) {
                        userId = user.getId();
                        return modelsApi.getUsersPrototypes(userId, 0, 0);
                    }
                })
                .subscribe(new Observer() {
                    @Override public void onCompleted() {}

                    @Override public void onError(Throwable e) {
                        if (e instanceof TimeoutException && retry <= 3) {
                            fetchUserModels(retry + 1);
                        } else {
                            System.err.println("DeviceModelCache - fetching user failed.");
                            stopRefreshing();
                        }
                    }

                    @Override public void onNext(DeviceModels models) {
                        offset = 0;
                        total = models.getCount();
                        fetchUserModels(userId, 0);
                    }
                });
    }

    private void fetchUserModels(final String userId, final int retry) {
        modelsApi.getUsersPrototypes(userId, LIMIT, offset)
                .subscribe(new Observer() {
                    @Override public void onCompleted() {}

                    @Override public void onError(Throwable e) {
                        if (e instanceof TimeoutException && retry <= 3) {
                            fetchUserModels(userId, retry + 1);
                        } else {
                            if (offset <= total) {
                                offset += LIMIT;
                                fetchUserModels(userId, 0);
                            } else {
                                System.err.println("DeviceModelCache - fetching user models failed.");
                                stopRefreshing();
                            }
                        }
                    }

                    @Override public void onNext(DeviceModels models) {
                        if (models == null || models.getPrototypes() == null) return;

                        System.out.println("DeviceModelCache - loaded " +
                                models.getPrototypes().size() + " user models.");

                        for (DeviceModel deviceModel : models.getPrototypes())
                            sDeviceModels.put(deviceModel.getId(), deviceModel);

                        offset += LIMIT;
                        if (offset <= total) fetchUserModels(userId, 0);
                        else stopRefreshing();
                    }
                });
    }

    private void stopRefreshing() {
        offset = 0;
        refreshing = false;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy