]> git.basschouten.com Git - openhab-addons.git/blob
d78381c9bef1eb529579e9ef69efbfbf25645104
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.persistence.mapdb.internal;
14
15 import java.io.File;
16 import java.util.Date;
17 import java.util.List;
18 import java.util.Locale;
19 import java.util.Map;
20 import java.util.Optional;
21 import java.util.Set;
22 import java.util.concurrent.ExecutorService;
23 import java.util.stream.Collectors;
24 import java.util.stream.Stream;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.mapdb.DB;
29 import org.mapdb.DBMaker;
30 import org.openhab.core.OpenHAB;
31 import org.openhab.core.common.ThreadPoolManager;
32 import org.openhab.core.items.Item;
33 import org.openhab.core.persistence.FilterCriteria;
34 import org.openhab.core.persistence.HistoricItem;
35 import org.openhab.core.persistence.PersistenceItemInfo;
36 import org.openhab.core.persistence.PersistenceService;
37 import org.openhab.core.persistence.QueryablePersistenceService;
38 import org.openhab.core.persistence.strategy.PersistenceStrategy;
39 import org.openhab.core.types.State;
40 import org.openhab.core.types.UnDefType;
41 import org.osgi.service.component.annotations.Activate;
42 import org.osgi.service.component.annotations.Component;
43 import org.osgi.service.component.annotations.Deactivate;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 import com.google.gson.Gson;
48 import com.google.gson.GsonBuilder;
49
50 /**
51  * This is the implementation of the MapDB {@link PersistenceService}. To learn more about MapDB please visit their
52  * <a href="http://www.mapdb.org/">website</a>.
53  *
54  * @author Jens Viebig - Initial contribution
55  * @author Martin Kühl - Port to 3.x
56  */
57 @NonNullByDefault
58 @Component(service = { PersistenceService.class, QueryablePersistenceService.class })
59 public class MapDbPersistenceService implements QueryablePersistenceService {
60
61     private static final String SERVICE_ID = "mapdb";
62     private static final String SERVICE_LABEL = "MapDB";
63     private static final String DB_FOLDER_NAME = OpenHAB.getUserDataFolder() + File.separator + "persistence"
64             + File.separator + "mapdb";
65     private static final String DB_FILE_NAME = "storage.mapdb";
66
67     private final Logger logger = LoggerFactory.getLogger(MapDbPersistenceService.class);
68
69     private final ExecutorService threadPool = ThreadPoolManager.getPool(getClass().getSimpleName());
70
71     /** holds the local instance of the MapDB database */
72
73     private @NonNullByDefault({}) DB db;
74     private @NonNullByDefault({}) Map<String, String> map;
75
76     private transient Gson mapper = new GsonBuilder().registerTypeHierarchyAdapter(State.class, new StateTypeAdapter())
77             .create();
78
79     @Activate
80     public void activate() {
81         logger.debug("MapDB persistence service is being activated");
82
83         File folder = new File(DB_FOLDER_NAME);
84         if (!folder.exists()) {
85             if (!folder.mkdirs()) {
86                 logger.warn("Failed to create one or more directories in the path '{}'", DB_FOLDER_NAME);
87                 logger.warn("MapDB persistence service activation has failed.");
88                 return;
89             }
90         }
91
92         File dbFile = new File(DB_FOLDER_NAME, DB_FILE_NAME);
93         db = DBMaker.newFileDB(dbFile).closeOnJvmShutdown().make();
94         map = db.createTreeMap("itemStore").makeOrGet();
95         logger.debug("MapDB persistence service is now activated");
96     }
97
98     @Deactivate
99     public void deactivate() {
100         logger.debug("MapDB persistence service deactivated");
101         if (db != null) {
102             db.close();
103         }
104     }
105
106     @Override
107     public String getId() {
108         return SERVICE_ID;
109     }
110
111     @Override
112     public String getLabel(@Nullable Locale locale) {
113         return SERVICE_LABEL;
114     }
115
116     @Override
117     public Set<PersistenceItemInfo> getItemInfo() {
118         return map.values().stream().map(this::deserialize).flatMap(MapDbPersistenceService::streamOptional)
119                 .collect(Collectors.<PersistenceItemInfo> toUnmodifiableSet());
120     }
121
122     @Override
123     public void store(Item item) {
124         store(item, item.getName());
125     }
126
127     @Override
128     public void store(Item item, @Nullable String alias) {
129         if (item.getState() instanceof UnDefType) {
130             return;
131         }
132
133         // PersistenceManager passes SimpleItemConfiguration.alias which can be null
134         String localAlias = alias == null ? item.getName() : alias;
135         logger.debug("store called for {}", localAlias);
136
137         State state = item.getState();
138         MapDbItem mItem = new MapDbItem();
139         mItem.setName(localAlias);
140         mItem.setState(state);
141         mItem.setTimestamp(new Date());
142         String json = serialize(mItem);
143         map.put(localAlias, json);
144         commit();
145         if (logger.isDebugEnabled()) {
146             logger.debug("Stored '{}' with state '{}' as '{}' in MapDB database", localAlias, state, json);
147         }
148     }
149
150     @Override
151     public Iterable<HistoricItem> query(FilterCriteria filter) {
152         String json = map.get(filter.getItemName());
153         if (json == null) {
154             return List.of();
155         }
156         Optional<MapDbItem> item = deserialize(json);
157         return item.isPresent() ? List.of(item.get()) : List.of();
158     }
159
160     private String serialize(MapDbItem item) {
161         return mapper.toJson(item);
162     }
163
164     @SuppressWarnings("null")
165     private Optional<MapDbItem> deserialize(String json) {
166         MapDbItem item = mapper.<MapDbItem> fromJson(json, MapDbItem.class);
167         if (item == null || !item.isValid()) {
168             logger.warn("Deserialized invalid item: {}", item);
169             return Optional.empty();
170         } else if (logger.isDebugEnabled()) {
171             logger.debug("Deserialized '{}' with state '{}' from '{}'", item.getName(), item.getState(), json);
172         }
173
174         return Optional.of(item);
175     }
176
177     private void commit() {
178         threadPool.submit(() -> db.commit());
179     }
180
181     private static <T> Stream<T> streamOptional(Optional<T> opt) {
182         return opt.isPresent() ? Stream.of(opt.get()) : Stream.empty();
183     }
184
185     @Override
186     public List<PersistenceStrategy> getDefaultStrategies() {
187         return List.of(PersistenceStrategy.Globals.RESTORE, PersistenceStrategy.Globals.CHANGE);
188     }
189 }