blob: 366f42406a39fc7b9a4845e07090552502a517b0 [file] [log] [blame]
Aaron Kruglikov92511f22015-10-12 14:39:04 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Aaron Kruglikov92511f22015-10-12 14:39:04 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.persistence.impl;
18
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Service;
23import org.mapdb.DB;
24import org.mapdb.DBMaker;
25import org.onosproject.persistence.PersistenceService;
26import org.onosproject.persistence.PersistentMapBuilder;
27import org.onosproject.persistence.PersistentSetBuilder;
Yuta HIGUCHI7f2c6f92016-07-04 19:52:52 -070028import org.osgi.service.component.ComponentContext;
Aaron Kruglikov92511f22015-10-12 14:39:04 -070029import org.slf4j.Logger;
30
Yuta HIGUCHI7f2c6f92016-07-04 19:52:52 -070031import java.io.File;
Aaron Kruglikov92511f22015-10-12 14:39:04 -070032import java.io.IOException;
33import java.nio.file.Files;
34import java.nio.file.Path;
Aaron Kruglikov92511f22015-10-12 14:39:04 -070035import java.util.Map;
36import java.util.Set;
37import java.util.Timer;
38import java.util.TimerTask;
39
Heedo Kang4a47a302016-02-29 17:40:23 +090040import static org.onosproject.security.AppGuard.checkPermission;
41import static org.onosproject.security.AppPermission.Type.PERSISTENCE_WRITE;
Aaron Kruglikov92511f22015-10-12 14:39:04 -070042import static org.slf4j.LoggerFactory.getLogger;
43
44/**
45 * Service that maintains local disk backed maps and sets. This implementation automatically deletes empty structures
46 * on shutdown.
47 */
48@Component(immediate = true)
49@Service
50public class PersistenceManager implements PersistenceService {
51
Yuta HIGUCHI7f2c6f92016-07-04 19:52:52 -070052 private static final String DATABASE_PATH = "localDB";
Aaron Kruglikov92511f22015-10-12 14:39:04 -070053
54 static final String MAP_PREFIX = "map:";
55
56 static final String SET_PREFIX = "set:";
57
58 private final Logger log = getLogger(getClass());
59
60 private DB localDB = null;
61
62 private static final int FLUSH_FREQUENCY_MILLIS = 3000;
63
Aaron Kruglikovdfb325a2015-11-23 14:16:55 -080064 private Timer timer;
Aaron Kruglikov92511f22015-10-12 14:39:04 -070065
66 private final CommitTask commitTask = new CommitTask();
67
68 @Activate
Yuta HIGUCHI7f2c6f92016-07-04 19:52:52 -070069 public void activate(ComponentContext context) {
Aaron Kruglikovdfb325a2015-11-23 14:16:55 -080070 timer = new Timer();
Yuta HIGUCHI7f2c6f92016-07-04 19:52:52 -070071 // bundle's persistent storage area directory
72 File dbFolderPath = context.getBundleContext().getDataFile("");
73 Path dbPath = dbFolderPath.toPath().resolve(DATABASE_PATH);
74 log.debug("dbPath: {}", dbPath);
75
Aaron Kruglikov92511f22015-10-12 14:39:04 -070076 //Make sure the directory exists, if it does not, make it.
Yuta HIGUCHI7f2c6f92016-07-04 19:52:52 -070077 if (!dbFolderPath.isDirectory()) {
Aaron Kruglikov92511f22015-10-12 14:39:04 -070078 log.info("The specified folder location for the database did not exist and will be created.");
79 try {
Yuta HIGUCHI7f2c6f92016-07-04 19:52:52 -070080 Files.createDirectories(dbFolderPath.toPath());
Aaron Kruglikov92511f22015-10-12 14:39:04 -070081 } catch (IOException e) {
82 log.error("Could not create the required folder for the database.");
83 throw new PersistenceException("Database folder could not be created.");
84 }
85 }
86 //Notify if the database file does not exist.
87 boolean dbFound = Files.exists(dbPath);
88 if (!dbFound) {
89 log.info("The database file could not be located, a new database will be constructed.");
90
91 } else {
92 log.info("A previous database file has been found.");
93 }
94 localDB = DBMaker.newFileDB(dbPath.toFile())
95 .asyncWriteEnable()
96 .closeOnJvmShutdown()
97 .make();
98 timer.schedule(commitTask, FLUSH_FREQUENCY_MILLIS, FLUSH_FREQUENCY_MILLIS);
99 log.info("Started");
100 }
101
102 @Deactivate
103 public void deactivate() {
Aaron Kruglikovdfb325a2015-11-23 14:16:55 -0800104 timer.cancel();
Aaron Kruglikov92511f22015-10-12 14:39:04 -0700105 for (Map.Entry<String, Object> entry : localDB.getAll().entrySet()) {
106 String key = entry.getKey();
107 Object value = entry.getValue();
108 //This is a map implementation to be handled as such
109 if (value instanceof Map) {
110 Map asMap = (Map) value;
111 if (asMap.isEmpty()) {
112 //the map is empty and may be deleted
113 localDB.delete(key);
114 }
115 //This is a set implementation and can be handled as such
116 } else if (value instanceof Set) {
117 Set asSet = (Set) value;
118 if (asSet.isEmpty()) {
119 //the set is empty and may be deleted
120 localDB.delete(key);
121 }
122 }
123 }
124 localDB.commit();
125 localDB.close();
126 log.info("Stopped");
127 }
128
Yuta HIGUCHI7f2c6f92016-07-04 19:52:52 -0700129 @Override
Aaron Kruglikov92511f22015-10-12 14:39:04 -0700130 public <K, V> PersistentMapBuilder<K, V> persistentMapBuilder() {
Heedo Kang4a47a302016-02-29 17:40:23 +0900131 checkPermission(PERSISTENCE_WRITE);
Aaron Kruglikov92511f22015-10-12 14:39:04 -0700132 return new DefaultPersistentMapBuilder<>(localDB);
133 }
134
Yuta HIGUCHI7f2c6f92016-07-04 19:52:52 -0700135 @Override
Aaron Kruglikov92511f22015-10-12 14:39:04 -0700136 public <E> PersistentSetBuilder<E> persistentSetBuilder() {
Heedo Kang4a47a302016-02-29 17:40:23 +0900137 checkPermission(PERSISTENCE_WRITE);
Aaron Kruglikov92511f22015-10-12 14:39:04 -0700138 return new DefaultPersistentSetBuilder<>(localDB);
139 }
140
141 private class CommitTask extends TimerTask {
142
143 @Override
144 public void run() {
145 localDB.commit();
146 }
147 }
148}