removed unused mapdb storage bundle (#1488)
Signed-off-by: Kai Kreuzer <kai@openhab.org>pull/1493/head
parent
240c245b16
commit
97d7ef65a8
|
@ -532,12 +532,6 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.core.bundles</groupId>
|
||||
<artifactId>org.openhab.core.storage.mapdb</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
|
@ -1,23 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.core.storage.mapdb</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -1,14 +0,0 @@
|
|||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-core
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.core.bundles</groupId>
|
||||
<artifactId>org.openhab.core.reactor.bundles</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.core.storage.mapdb</artifactId>
|
||||
|
||||
<name>openHAB Core :: Bundles :: MapDB Storage</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.openhab.core.bundles</groupId>
|
||||
<artifactId>org.openhab.core.config.core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -1,189 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.core.storage.mapdb.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.mapdb.DB;
|
||||
import org.openhab.core.items.ManagedItemProvider.PersistedItem;
|
||||
import org.openhab.core.items.ManagedItemProvider.PersistedItemInstanceCreator;
|
||||
import org.openhab.core.storage.DeletableStorage;
|
||||
import org.openhab.core.storage.Storage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
* The MapDbStorage is concrete implementation of the {@link Storage} interface.
|
||||
* It stores the key-value pairs in files. This Storage serializes and deserializes
|
||||
* the given values using their JSON representation (generated by {@code Gson}.
|
||||
* This transformation should help maintaining version compatibility of the stored
|
||||
* data.
|
||||
*
|
||||
* @author Thomas Eichstaedt-Engelen - Initial contribution
|
||||
* @author Alex Tugarev - Loading with Class.forName() if classLoader is null
|
||||
* @author Markus Rathgeb - Made the MapDB storage a disposable one
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class MapDbStorage<T> implements DeletableStorage<T> {
|
||||
|
||||
static final String TYPE_SEPARATOR = "@@@";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MapDbStorage.class);
|
||||
|
||||
private final String name;
|
||||
private final DB db;
|
||||
private final @Nullable ClassLoader classLoader;
|
||||
private Map<String, String> map;
|
||||
|
||||
private transient Gson mapper;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param db the database
|
||||
* @param name the name
|
||||
* @param classLoader the classloader used for deserialization
|
||||
*/
|
||||
public MapDbStorage(final DB db, final String name, final @Nullable ClassLoader classLoader) {
|
||||
this.name = name;
|
||||
this.db = db;
|
||||
this.classLoader = classLoader;
|
||||
this.map = db.createTreeMap(name).makeOrGet();
|
||||
this.mapper = new GsonBuilder().registerTypeAdapterFactory(new PropertiesTypeAdapterFactory())
|
||||
.registerTypeAdapter(PersistedItem.class, new PersistedItemInstanceCreator()).create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
// Use an unmodifiable map. After deletion no operation / modification should be called anymore.
|
||||
map = Collections.emptyMap();
|
||||
db.delete(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable T put(String key, @Nullable T value) {
|
||||
if (value == null) {
|
||||
return remove(key);
|
||||
}
|
||||
String previousValue = map.put(key, serialize(value));
|
||||
db.commit();
|
||||
return deserialize(previousValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable T remove(String key) {
|
||||
String removedElement = map.remove(key);
|
||||
db.commit();
|
||||
return deserialize(removedElement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(final String key) {
|
||||
return map.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable T get(String key) {
|
||||
return deserialize(map.get(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getKeys() {
|
||||
return new HashSet<>(map.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<@Nullable T> getValues() {
|
||||
Collection<@Nullable T> values = new ArrayList<>();
|
||||
for (String key : getKeys()) {
|
||||
values.add(get(key));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the given {@code value} into its JSON representation using {@code Gson}.
|
||||
*
|
||||
* <p>
|
||||
* Since we do not know the type of {@code value} while deserializing it afterwards we prepend its qualified type
|
||||
* name to the JSON String.
|
||||
*
|
||||
* @param value the {@code value} to store
|
||||
* @return the JSON document prepended with the qualified type name of {@code value}
|
||||
*/
|
||||
private String serialize(T value) {
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("Cannot serialize NULL");
|
||||
}
|
||||
|
||||
String valueTypeName = value.getClass().getName();
|
||||
String valueAsString = mapper.toJson(value);
|
||||
String concatValue = valueTypeName + TYPE_SEPARATOR + valueAsString;
|
||||
|
||||
logger.trace("serialized value '{}' to MapDB", concatValue);
|
||||
return concatValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes and instantiates an object of type {@code T} out of the given JSON String.
|
||||
*
|
||||
* <p>
|
||||
* A special classloader (other than the one of the MapDB bundle) is used in order to load the classes in the
|
||||
* context of the calling bundle.
|
||||
*
|
||||
* @param json the JSON String
|
||||
* @return the deserialized object
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public @Nullable T deserialize(@Nullable String json) {
|
||||
if (json == null) {
|
||||
// nothing to deserialize
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] concatValue = json.split(TYPE_SEPARATOR, 2);
|
||||
String valueTypeName = concatValue[0];
|
||||
String valueAsString = concatValue[1];
|
||||
|
||||
@Nullable
|
||||
T value = null;
|
||||
try {
|
||||
final ClassLoader classLoader = this.classLoader;
|
||||
|
||||
// load required class within the given bundle context
|
||||
final Class<T> loadedValueType;
|
||||
if (classLoader == null) {
|
||||
loadedValueType = (Class<T>) Class.forName(valueTypeName);
|
||||
} else {
|
||||
loadedValueType = (Class<T>) classLoader.loadClass(valueTypeName);
|
||||
}
|
||||
|
||||
value = mapper.fromJson(valueAsString, loadedValueType);
|
||||
logger.trace("deserialized value '{}' from MapDB", value);
|
||||
} catch (final JsonSyntaxException | ClassNotFoundException ex) {
|
||||
logger.warn("Couldn't deserialize value '{}'. Root cause is: {}", json, ex.getMessage());
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.core.storage.mapdb.internal;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.mapdb.DB;
|
||||
import org.mapdb.DBMaker;
|
||||
import org.openhab.core.config.core.ConfigConstants;
|
||||
import org.openhab.core.storage.DeletableStorage;
|
||||
import org.openhab.core.storage.DeletableStorageService;
|
||||
import org.openhab.core.storage.StorageService;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Deactivate;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This implementation of {@link StorageService} provides abilities to store
|
||||
* data in the lightweight key-value-store <a href="http://www.mapdb.org">MapDB</a>.
|
||||
*
|
||||
* @author Thomas Eichstaedt-Engelen - Initial contribution
|
||||
* @author Alex Tugarev - Added getStorage for name only
|
||||
* @author Markus Rathgeb - Use {@link DeletableStorageService}
|
||||
*/
|
||||
@Component(name = "org.openhab.core.storage.mapdb", configurationPid = "org.openhab.storage.mapdb", immediate = true, service = {
|
||||
StorageService.class, DeletableStorageService.class }, property = "storage.format=mapdb")
|
||||
@NonNullByDefault
|
||||
public class MapDbStorageService implements DeletableStorageService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(MapDbStorageService.class);
|
||||
|
||||
/* the name of the mapdb database ({@code storage.mapdb}) */
|
||||
private static final String DB_FILE_NAME = "storage.mapdb";
|
||||
|
||||
/* holds the local instance of the MapDB database */
|
||||
private final DB db;
|
||||
|
||||
/* the folder name to store mapdb databases ({@code mapdb} by default) */
|
||||
private String dbFolderName = "mapdb";
|
||||
|
||||
@Activate
|
||||
public MapDbStorageService() {
|
||||
dbFolderName = ConfigConstants.getUserDataFolder() + File.separator + dbFolderName;
|
||||
File folder = new File(dbFolderName);
|
||||
if (!folder.exists()) {
|
||||
folder.mkdirs();
|
||||
}
|
||||
|
||||
File dbFile = new File(dbFolderName, DB_FILE_NAME);
|
||||
db = DBMaker.newFileDB(dbFile).closeOnJvmShutdown().make();
|
||||
|
||||
logger.debug("Opened MapDB file at '{}'.", dbFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
public void deactivate() {
|
||||
db.close();
|
||||
logger.debug("Deactivated MapDB Storage Service.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DeletableStorage<T> getStorage(String name, @Nullable ClassLoader classLoader) {
|
||||
return new MapDbStorage<>(db, name, classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DeletableStorage<T> getStorage(String name) {
|
||||
return getStorage(name, null);
|
||||
}
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.core.storage.mapdb.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.InstanceCreator;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.internal.ConstructorConstructor;
|
||||
import com.google.gson.internal.JsonReaderInternalAccess;
|
||||
import com.google.gson.internal.bind.MapTypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
/**
|
||||
* Type adapter that makes sure that all Numeric values in Maps of type Map<String, Object> are deserialized as
|
||||
* BigDecimal instances instead of doubles.
|
||||
*
|
||||
* @author Ivan Iliev - Initial contribution
|
||||
*/
|
||||
public class PropertiesTypeAdapter extends TypeAdapter<Map<String, Object>> {
|
||||
|
||||
/** Type token. */
|
||||
public static final TypeToken<Map<String, Object>> TOKEN = new TypeToken<Map<String, Object>>() {
|
||||
};
|
||||
|
||||
private final TypeAdapter<Map<String, Object>> delegate;
|
||||
|
||||
private final ConstructorConstructor constructor;
|
||||
|
||||
private final TypeAdapter<String> keyAdapter;
|
||||
|
||||
private final TypeAdapter<Object> valueAdapter;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param gson the Gson reference
|
||||
*/
|
||||
public PropertiesTypeAdapter(final Gson gson) {
|
||||
// obtain the default type adapters for String and Object classes
|
||||
keyAdapter = gson.getAdapter(String.class);
|
||||
valueAdapter = gson.getAdapter(Object.class);
|
||||
|
||||
// obtain default gson objects
|
||||
constructor = new ConstructorConstructor(Collections.<Type, InstanceCreator<?>> emptyMap());
|
||||
delegate = new MapTypeAdapterFactory(constructor, false).create(new Gson(), TOKEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter out, @Nullable Map<String, Object> value) throws IOException {
|
||||
// write remains unchanged
|
||||
delegate.write(out, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Map<String, Object> read(JsonReader in) throws IOException {
|
||||
// gson implementation code is modified when deserializing numbers
|
||||
JsonToken peek = in.peek();
|
||||
if (peek == JsonToken.NULL) {
|
||||
in.nextNull();
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, Object> map = constructor.get(TOKEN).construct();
|
||||
|
||||
if (peek == JsonToken.BEGIN_ARRAY) {
|
||||
in.beginArray();
|
||||
while (in.hasNext()) {
|
||||
in.beginArray(); // entry array
|
||||
String key = keyAdapter.read(in);
|
||||
|
||||
// modification
|
||||
Object value = getValue(in);
|
||||
|
||||
Object replaced = map.put(key, value);
|
||||
if (replaced != null) {
|
||||
throw new JsonSyntaxException("duplicate key: " + key);
|
||||
}
|
||||
in.endArray();
|
||||
}
|
||||
in.endArray();
|
||||
} else {
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
|
||||
String key = keyAdapter.read(in);
|
||||
|
||||
// modification
|
||||
Object value = getValue(in);
|
||||
|
||||
Object replaced = map.put(key, value);
|
||||
if (replaced != null) {
|
||||
throw new JsonSyntaxException("duplicate key: " + key);
|
||||
}
|
||||
}
|
||||
in.endObject();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private Object getValue(JsonReader in) throws IOException {
|
||||
Object value = null;
|
||||
|
||||
// if the next json token is a number we read it as a BigDecimal,
|
||||
// otherwise use the default adapter to read it
|
||||
if (JsonToken.NUMBER.equals(in.peek())) {
|
||||
value = new BigDecimal(in.nextString());
|
||||
} else {
|
||||
value = valueAdapter.read(in);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.core.storage.mapdb.internal;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* TypeAdapterFactory responsible for returning a new instance of {@link PropertiesTypeAdapter} if the given type
|
||||
* matches Map<String, Object> or null otherwise.
|
||||
*
|
||||
* @author Ivan Iliev - Initial contribution
|
||||
*/
|
||||
public class PropertiesTypeAdapterFactory implements TypeAdapterFactory {
|
||||
|
||||
@SuppressWarnings({ "unused", "unchecked" })
|
||||
@Override
|
||||
public <T> @Nullable TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
Type type = typeToken.getType();
|
||||
|
||||
Class<? super T> rawType = typeToken.getRawType();
|
||||
if (!PropertiesTypeAdapter.TOKEN.equals(typeToken)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (TypeAdapter<T>) new PropertiesTypeAdapter(gson);
|
||||
}
|
||||
}
|
|
@ -1,266 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.core.storage.mapdb.internal;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.openhab.core.config.core.ConfigConstants;
|
||||
import org.openhab.core.library.CoreItemFactory;
|
||||
import org.openhab.core.storage.DeletableStorage;
|
||||
import org.openhab.core.storage.Storage;
|
||||
|
||||
/**
|
||||
* @author Thomas Eichstaedt-Engelen - Initial contribution
|
||||
* @author Alex Tugarev - Added test for getStorage without classloader
|
||||
* @author Markus Rathgeb - Migrate Groovy tests to OSGi
|
||||
* @author Markus Rathgeb - Migrate OSGi test to non-OSGi test
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class MapDbStorageServiceTest {
|
||||
|
||||
private static final String KEY_1 = "Key1";
|
||||
private static final String KEY_2 = "Key2";
|
||||
|
||||
private static class PersistedItem {
|
||||
public final String itemType;
|
||||
public final List<String> groupNames;
|
||||
public final @Nullable String baseItemType;
|
||||
|
||||
public PersistedItem(String itemType, List<String> groupNames) {
|
||||
this(itemType, groupNames, null);
|
||||
}
|
||||
|
||||
public PersistedItem(String itemType, List<String> groupNames, @Nullable String baseItemType) {
|
||||
this.itemType = itemType;
|
||||
this.groupNames = groupNames;
|
||||
this.baseItemType = baseItemType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PersistedItem [itemType=%s, groupNames=%s, baseItemType=%s]", itemType, groupNames,
|
||||
baseItemType);
|
||||
}
|
||||
}
|
||||
|
||||
private static class MockConfiguration {
|
||||
private final Map<String, Object> configuration = new HashMap<>();
|
||||
|
||||
public void put(String key, Object value) {
|
||||
configuration.put(key, value);
|
||||
}
|
||||
|
||||
public @Nullable Object get(String key) {
|
||||
return configuration.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
public static class EntryTypeSeparatorTest {
|
||||
public int num;
|
||||
public @Nullable String str;
|
||||
public boolean bool;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Entry [num=" + num + ", str=" + str + ", bool=" + bool + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (bool ? 1231 : 1237);
|
||||
result = prime * result + num;
|
||||
final String str = this.str;
|
||||
result = prime * result + ((str == null) ? 0 : str.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
EntryTypeSeparatorTest other = (EntryTypeSeparatorTest) obj;
|
||||
if (bool != other.bool) {
|
||||
return false;
|
||||
}
|
||||
if (num != other.num) {
|
||||
return false;
|
||||
}
|
||||
if (str == null) {
|
||||
if (other.str != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!Objects.equals(str, other.str)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private @NonNullByDefault({}) Path tmpDir;
|
||||
private @NonNullByDefault({}) MapDbStorageService storageService;
|
||||
private @NonNullByDefault({}) MapDbStorage<Object> storage;
|
||||
|
||||
@Before
|
||||
public void setup() throws IOException {
|
||||
tmpDir = Files.createTempDirectory(null);
|
||||
final Path userdata = tmpDir.resolve("userdata");
|
||||
userdata.toFile().mkdir();
|
||||
System.setProperty(ConfigConstants.USERDATA_DIR_PROG_ARGUMENT, userdata.toString());
|
||||
|
||||
storageService = new MapDbStorageService();
|
||||
this.storage = (MapDbStorage<Object>) storageService.getStorage("TestStorage", getClass().getClassLoader());
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() throws IOException {
|
||||
if (storage != null) {
|
||||
storage.delete();
|
||||
storage = null;
|
||||
}
|
||||
if (storageService != null) {
|
||||
storageService.deactivate();
|
||||
storageService = null;
|
||||
}
|
||||
|
||||
// clean up database files ...
|
||||
removeDirRecursive(tmpDir);
|
||||
}
|
||||
|
||||
private static void removeDirRecursive(final Path path) throws IOException {
|
||||
if (Files.exists(path)) {
|
||||
Files.walk(path).map(Path::toFile).sorted((o1, o2) -> -o1.compareTo(o2)).forEach(File::delete);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert elements are serialized and deserialized by the storage.
|
||||
*/
|
||||
@Test
|
||||
public void serializationDeserialization() {
|
||||
Assert.assertThat(storage.getKeys().size(), Matchers.equalTo(0));
|
||||
storage.put(KEY_1, new PersistedItem(CoreItemFactory.STRING, Arrays.asList("LIGHT", "GROUND_FLOOR")));
|
||||
storage.put(KEY_2, new PersistedItem(CoreItemFactory.NUMBER, Arrays.asList("TEMPERATURE", "OUTSIDE")));
|
||||
Assert.assertThat(storage.getKeys().size(), Matchers.equalTo(2));
|
||||
final Object persistedObject = storage.get(KEY_1);
|
||||
Assert.assertThat(persistedObject, Matchers.instanceOf(PersistedItem.class));
|
||||
storage.remove(KEY_1);
|
||||
storage.remove(KEY_2);
|
||||
Assert.assertThat(storage.getKeys().size(), Matchers.equalTo(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert old element gets overwritten when new value is stored under an existing key.
|
||||
*/
|
||||
@Test
|
||||
public void override() {
|
||||
Object persistedObject = null;
|
||||
PersistedItem persistedItem = null;
|
||||
|
||||
Assert.assertEquals(0, storage.getKeys().size());
|
||||
|
||||
persistedObject = storage.put(KEY_1,
|
||||
new PersistedItem(CoreItemFactory.STRING, Arrays.asList("LIGHT", "GROUND_FLOOR")));
|
||||
Assert.assertEquals(1, storage.getKeys().size());
|
||||
Assert.assertNull(persistedObject);
|
||||
|
||||
persistedObject = storage.get(KEY_1);
|
||||
Assert.assertTrue(persistedObject instanceof PersistedItem);
|
||||
persistedItem = (PersistedItem) persistedObject;
|
||||
Assert.assertEquals(CoreItemFactory.STRING, persistedItem.itemType);
|
||||
|
||||
persistedObject = storage.put(KEY_1, new PersistedItem(CoreItemFactory.NUMBER, Arrays.asList("TEMPERATURE")));
|
||||
Assert.assertTrue(persistedObject instanceof PersistedItem);
|
||||
persistedItem = (PersistedItem) persistedObject;
|
||||
Assert.assertEquals(1, storage.getKeys().size());
|
||||
Assert.assertEquals(CoreItemFactory.STRING, persistedItem.itemType);
|
||||
|
||||
persistedObject = storage.get(KEY_1);
|
||||
Assert.assertTrue(persistedObject instanceof PersistedItem);
|
||||
persistedItem = (PersistedItem) persistedObject;
|
||||
Assert.assertEquals(CoreItemFactory.NUMBER, persistedItem.itemType);
|
||||
|
||||
storage.remove(KEY_1);
|
||||
Assert.assertEquals(0, storage.getKeys().size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert storage works without classloader.
|
||||
*/
|
||||
@Test
|
||||
public void withoutClassloader() {
|
||||
final Storage<String> storageWithoutClassloader = storageService.getStorage("storageWithoutClassloader");
|
||||
final String value = "Value";
|
||||
storageWithoutClassloader.put(KEY_1, value);
|
||||
Assert.assertEquals(value, storageWithoutClassloader.get(KEY_1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert store configuration works.
|
||||
*/
|
||||
@Test
|
||||
public void storeConfiguration() {
|
||||
final Storage<MockConfiguration> storageWithoutClassloader = storageService.getStorage("storage");
|
||||
final MockConfiguration configuration = new MockConfiguration();
|
||||
configuration.put(KEY_1, new BigDecimal(3));
|
||||
storageWithoutClassloader.put(KEY_2, configuration);
|
||||
final Object persistedObject = storageWithoutClassloader.get(KEY_2);
|
||||
Assert.assertTrue(persistedObject instanceof MockConfiguration);
|
||||
final MockConfiguration persistedConfiguration = (MockConfiguration) persistedObject;
|
||||
final Object cfgValue = persistedConfiguration.get(KEY_1);
|
||||
Assert.assertTrue(cfgValue instanceof BigDecimal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the usage of the type separator does not break the storage.
|
||||
*/
|
||||
@Test
|
||||
public void typeSeparator() {
|
||||
final DeletableStorage<EntryTypeSeparatorTest> storage = storageService.getStorage("type_separator");
|
||||
try {
|
||||
final EntryTypeSeparatorTest entryOriginal = new EntryTypeSeparatorTest();
|
||||
entryOriginal.num = 2810;
|
||||
entryOriginal.str = MapDbStorage.TYPE_SEPARATOR;
|
||||
entryOriginal.bool = true;
|
||||
storage.put(KEY_1, entryOriginal);
|
||||
final EntryTypeSeparatorTest entryStorage = storage.get(KEY_1);
|
||||
Assert.assertThat(entryStorage, Matchers.equalTo(entryOriginal));
|
||||
} finally {
|
||||
storage.delete();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -100,7 +100,6 @@
|
|||
<module>org.openhab.core.model.thing.ide</module>
|
||||
<module>org.openhab.core.model.thing.runtime</module>
|
||||
<module>org.openhab.core.storage.json</module>
|
||||
<module>org.openhab.core.storage.mapdb</module>
|
||||
<module>org.openhab.core.test</module>
|
||||
<module>org.openhab.core.test.magic</module>
|
||||
<module>org.openhab.core.ui</module>
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
<bundle>mvn:org.openhab.core.bundles/org.openhab.core.config.dispatch/${project.version}</bundle>
|
||||
<bundle start-level="75">mvn:org.openhab.core.bundles/org.openhab.core.config.xml/${project.version}</bundle>
|
||||
<bundle>mvn:org.openhab.core.bundles/org.openhab.core/${project.version}</bundle>
|
||||
<feature dependency="true">openhab-core-storage-mapdb</feature>
|
||||
<feature dependency="true">openhab-core-storage-json</feature>
|
||||
<bundle>mvn:org.openhab.core.bundles/org.openhab.core.binding.xml/${project.version}</bundle>
|
||||
<bundle>mvn:org.openhab.core.bundles/org.openhab.core.id/${project.version}</bundle>
|
||||
<bundle>mvn:org.openhab.core.bundles/org.openhab.core.persistence/${project.version}</bundle>
|
||||
|
@ -404,15 +404,6 @@
|
|||
<bundle>mvn:org.openhab.core.bundles/org.openhab.core.model.lsp/${project.version}</bundle>
|
||||
</feature>
|
||||
|
||||
<feature name="openhab-core-storage-mapdb" version="${project.version}">
|
||||
<feature>openhab-core-base</feature>
|
||||
|
||||
<bundle>mvn:org.openhab.core.bundles/org.openhab.core.storage.mapdb/${project.version}</bundle>
|
||||
|
||||
<requirement>openhab.tp;filter:="(feature=mapdb)"</requirement>
|
||||
<feature dependency="true">openhab.tp-mapdb</feature>
|
||||
</feature>
|
||||
|
||||
<feature name="openhab-core-storage-json" version="${project.version}">
|
||||
<feature>openhab-core-base</feature>
|
||||
|
||||
|
|
|
@ -2,5 +2,4 @@
|
|||
|
||||
# If we would like to use a storage at all, we will use the "volatile" storage.
|
||||
-runblacklist.itest-common: \
|
||||
bnd.identity;id='org.openhab.core.storage.json',\
|
||||
bnd.identity;id='org.openhab.core.storage.mapdb'
|
||||
bnd.identity;id='org.openhab.core.storage.json'
|
||||
|
|
Loading…
Reference in New Issue