
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