Codebase as of c53e4aed26 as an initial commit for the shrunk repo

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2010-02-20 19:23:32 +01:00
committed by Kai Kreuzer
commit bbf1a7fd29
302 changed files with 29726 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.persistence.mapdb-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-persistence-mapdb" description="MapDB Persistence" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.persistence.mapdb/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,90 @@
/**
* 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.persistence.mapdb.internal;
import java.text.DateFormat;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.persistence.HistoricItem;
import org.openhab.core.persistence.PersistenceItemInfo;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/**
* This is a Java bean used to persist item states with timestamps in the database.
*
* @author Jens Viebig - Initial contribution
*
*/
@NonNullByDefault
public class MapDbItem implements HistoricItem, PersistenceItemInfo {
private String name = "";
private State state = UnDefType.NULL;
private Date timestamp = new Date(0);
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
@Override
public ZonedDateTime getTimestamp() {
return ZonedDateTime.ofInstant(timestamp.toInstant(), ZoneId.systemDefault());
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
@Override
public String toString() {
return DateFormat.getDateTimeInstance().format(timestamp) + ": " + name + " -> " + state.toString();
}
@Override
public @Nullable Integer getCount() {
return Integer.valueOf(1);
}
@Override
public @Nullable Date getEarliest() {
return null;
}
@Override
public @Nullable Date getLatest() {
return null;
}
public boolean isValid() {
return name != null && state != null && timestamp != null;
}
}

View File

@@ -0,0 +1,192 @@
/**
* 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.persistence.mapdb.internal;
import java.io.File;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.mapdb.DB;
import org.mapdb.DBMaker;
import org.openhab.core.OpenHAB;
import org.openhab.core.common.ThreadPoolManager;
import org.openhab.core.items.Item;
import org.openhab.core.persistence.FilterCriteria;
import org.openhab.core.persistence.HistoricItem;
import org.openhab.core.persistence.PersistenceItemInfo;
import org.openhab.core.persistence.PersistenceService;
import org.openhab.core.persistence.QueryablePersistenceService;
import org.openhab.core.persistence.strategy.PersistenceStrategy;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
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;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
* This is the implementation of the MapDB {@link PersistenceService}. To learn more about MapDB please visit their
* <a href="http://www.mapdb.org/">website</a>.
*
* @author Jens Viebig - Initial contribution
* @author Martin Kühl - Port to 3.x
*/
@NonNullByDefault
@Component(service = { PersistenceService.class, QueryablePersistenceService.class })
public class MapDbPersistenceService implements QueryablePersistenceService {
private static final String SERVICE_ID = "mapdb";
private static final String SERVICE_LABEL = "MapDB";
private static final String DB_FOLDER_NAME = OpenHAB.getUserDataFolder() + File.separator + "persistence"
+ File.separator + "mapdb";
private static final String DB_FILE_NAME = "storage.mapdb";
private final Logger logger = LoggerFactory.getLogger(MapDbPersistenceService.class);
private final ExecutorService threadPool = ThreadPoolManager.getPool(getClass().getSimpleName());
/** holds the local instance of the MapDB database */
private @NonNullByDefault({}) DB db;
private @NonNullByDefault({}) Map<String, String> map;
private transient Gson mapper = new GsonBuilder().registerTypeHierarchyAdapter(State.class, new StateTypeAdapter())
.create();
@Activate
public void activate() {
logger.debug("MapDB persistence service is being activated");
File folder = new File(DB_FOLDER_NAME);
if (!folder.exists()) {
if (!folder.mkdirs()) {
logger.warn("Failed to create one or more directories in the path '{}'", DB_FOLDER_NAME);
logger.warn("MapDB persistence service activation has failed.");
return;
}
}
File dbFile = new File(DB_FOLDER_NAME, DB_FILE_NAME);
db = DBMaker.newFileDB(dbFile).closeOnJvmShutdown().make();
map = db.createTreeMap("itemStore").makeOrGet();
logger.debug("MapDB persistence service is now activated");
}
@Deactivate
public void deactivate() {
logger.debug("MapDB persistence service deactivated");
if (db != null) {
db.close();
}
threadPool.shutdown();
}
@Override
public String getId() {
return SERVICE_ID;
}
@Override
public String getLabel(@Nullable Locale locale) {
return SERVICE_LABEL;
}
@Override
public Set<PersistenceItemInfo> getItemInfo() {
return map.values().stream().map(this::deserialize).flatMap(MapDbPersistenceService::streamOptional)
.collect(Collectors.<PersistenceItemInfo> toUnmodifiableSet());
}
@Override
public void store(Item item) {
store(item, item.getName());
}
@Override
public void store(Item item, @Nullable String alias) {
if (item.getState() instanceof UnDefType) {
return;
}
// PersistenceManager passes SimpleItemConfiguration.alias which can be null
String localAlias = alias == null ? item.getName() : alias;
logger.debug("store called for {}", localAlias);
State state = item.getState();
MapDbItem mItem = new MapDbItem();
mItem.setName(localAlias);
mItem.setState(state);
mItem.setTimestamp(new Date());
String json = serialize(mItem);
map.put(localAlias, json);
commit();
logger.debug("Stored '{}' with state '{}' in MapDB database", localAlias, state.toString());
}
@Override
public Iterable<HistoricItem> query(FilterCriteria filter) {
String json = map.get(filter.getItemName());
if (json == null) {
return Collections.emptyList();
}
Optional<MapDbItem> item = deserialize(json);
if (!item.isPresent()) {
return Collections.emptyList();
}
return Collections.singletonList(item.get());
}
private String serialize(MapDbItem item) {
return mapper.toJson(item);
}
@SuppressWarnings("null")
private Optional<MapDbItem> deserialize(String json) {
MapDbItem item = mapper.<MapDbItem> fromJson(json, MapDbItem.class);
if (item == null || !item.isValid()) {
logger.warn("Deserialized invalid item: {}", item);
return Optional.empty();
}
return Optional.of(item);
}
private void commit() {
threadPool.submit(() -> db.commit());
}
private static <T> Stream<T> streamOptional(Optional<T> opt) {
if (!opt.isPresent()) {
return Stream.empty();
}
return Stream.of(opt.get());
}
@Override
public List<PersistenceStrategy> getDefaultStrategies() {
return List.of(PersistenceStrategy.Globals.RESTORE, PersistenceStrategy.Globals.CHANGE);
}
}

View File

@@ -0,0 +1,70 @@
/**
* 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.persistence.mapdb.internal;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import org.openhab.core.types.State;
import org.openhab.core.types.TypeParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
/**
* A GSON TypeAdapter for openHAB State values.
*
* @author Martin Kühl - Initial contribution
*/
public class StateTypeAdapter extends TypeAdapter<State> {
private static final String TYPE_SEPARATOR = "@@@";
private final Logger logger = LoggerFactory.getLogger(StateTypeAdapter.class);
@Override
public State read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
String value = reader.nextString();
String[] parts = value.split(TYPE_SEPARATOR);
String valueTypeName = parts[0];
String valueAsString = parts[1];
try {
@SuppressWarnings("unchecked")
Class<? extends State> valueType = (Class<? extends State>) Class.forName(valueTypeName);
List<Class<? extends State>> types = Collections.singletonList(valueType);
return TypeParser.parseState(types, valueAsString);
} catch (Exception e) {
logger.warn("Couldn't deserialize state '{}': {}", value, e.getMessage());
}
return null;
}
@Override
public void write(JsonWriter writer, State state) throws IOException {
if (state == null) {
writer.nullValue();
return;
}
String value = state.getClass().getName() + TYPE_SEPARATOR + state.toFullString();
writer.value(value);
}
}

View File

@@ -0,0 +1,47 @@
/**
* 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.persistence.mapdb;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import org.junit.jupiter.api.Test;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.State;
import org.openhab.persistence.mapdb.internal.StateTypeAdapter;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
*
* @author Martin Kühl - Initial contribution
*/
public class StateTypeAdapterTest {
Gson mapper = new GsonBuilder().registerTypeHierarchyAdapter(State.class, new StateTypeAdapter()).create();
@Test
public void readWriteRoundtripShouldRecreateTheWrittenState() {
assertThat(roundtrip(OnOffType.ON), is(equalTo(OnOffType.ON)));
assertThat(roundtrip(PercentType.HUNDRED), is(equalTo(PercentType.HUNDRED)));
assertThat(roundtrip(HSBType.GREEN), is(equalTo(HSBType.GREEN)));
assertThat(roundtrip(StringType.valueOf("test")), is(equalTo(StringType.valueOf("test"))));
}
private State roundtrip(State state) {
return mapper.fromJson(mapper.toJson(state), State.class);
}
}