mirror of
https://github.com/danieldemus/openhab-core.git
synced 2025-02-04 08:03:53 +01:00
removed unused mapdb storage bundle (#1488)
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
parent
240c245b16
commit
97d7ef65a8
@ -532,12 +532,6 @@
|
|||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.openhab.core.bundles</groupId>
|
|
||||||
<artifactId>org.openhab.core.storage.mapdb</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
<scope>compile</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</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.ide</module>
|
||||||
<module>org.openhab.core.model.thing.runtime</module>
|
<module>org.openhab.core.model.thing.runtime</module>
|
||||||
<module>org.openhab.core.storage.json</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</module>
|
||||||
<module>org.openhab.core.test.magic</module>
|
<module>org.openhab.core.test.magic</module>
|
||||||
<module>org.openhab.core.ui</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>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 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>
|
<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.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.id/${project.version}</bundle>
|
||||||
<bundle>mvn:org.openhab.core.bundles/org.openhab.core.persistence/${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>
|
<bundle>mvn:org.openhab.core.bundles/org.openhab.core.model.lsp/${project.version}</bundle>
|
||||||
</feature>
|
</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 name="openhab-core-storage-json" version="${project.version}">
|
||||||
<feature>openhab-core-base</feature>
|
<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.
|
# If we would like to use a storage at all, we will use the "volatile" storage.
|
||||||
-runblacklist.itest-common: \
|
-runblacklist.itest-common: \
|
||||||
bnd.identity;id='org.openhab.core.storage.json',\
|
bnd.identity;id='org.openhab.core.storage.json'
|
||||||
bnd.identity;id='org.openhab.core.storage.mapdb'
|
|
||||||
|
Loading…
Reference in New Issue
Block a user