/* * Copyright 2015 Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onosproject.persistence.impl; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Service; import org.mapdb.DB; import org.mapdb.DBMaker; import org.onosproject.persistence.PersistenceService; import org.onosproject.persistence.PersistentMapBuilder; import org.onosproject.persistence.PersistentSetBuilder; import org.slf4j.Logger; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import static org.slf4j.LoggerFactory.getLogger; /** * Service that maintains local disk backed maps and sets. This implementation automatically deletes empty structures * on shutdown. */ @Component(immediate = true) @Service public class PersistenceManager implements PersistenceService { private static final String DATABASE_PATH = "../data/localDB"; private static final String ENCLOSING_FOLDER = "../data"; static final String MAP_PREFIX = "map:"; static final String SET_PREFIX = "set:"; private final Logger log = getLogger(getClass()); private DB localDB = null; private static final int FLUSH_FREQUENCY_MILLIS = 3000; private final Timer timer = new Timer(); private final CommitTask commitTask = new CommitTask(); @Activate public void activate() { Path dbPath = Paths.get(DATABASE_PATH); Path dbFolderPath = Paths.get(ENCLOSING_FOLDER); //Make sure the directory exists, if it does not, make it. if (!dbFolderPath.toFile().isDirectory()) { log.info("The specified folder location for the database did not exist and will be created."); try { Files.createDirectories(dbFolderPath); } catch (IOException e) { log.error("Could not create the required folder for the database."); throw new PersistenceException("Database folder could not be created."); } } //Notify if the database file does not exist. boolean dbFound = Files.exists(dbPath); if (!dbFound) { log.info("The database file could not be located, a new database will be constructed."); } else { log.info("A previous database file has been found."); } localDB = DBMaker.newFileDB(dbPath.toFile()) .asyncWriteEnable() .closeOnJvmShutdown() .make(); timer.schedule(commitTask, FLUSH_FREQUENCY_MILLIS, FLUSH_FREQUENCY_MILLIS); log.info("Started"); } @Deactivate public void deactivate() { for (Map.Entry entry : localDB.getAll().entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); //This is a map implementation to be handled as such if (value instanceof Map) { Map asMap = (Map) value; if (asMap.isEmpty()) { //the map is empty and may be deleted localDB.delete(key); } //This is a set implementation and can be handled as such } else if (value instanceof Set) { Set asSet = (Set) value; if (asSet.isEmpty()) { //the set is empty and may be deleted localDB.delete(key); } } } localDB.commit(); localDB.close(); log.info("Stopped"); } public PersistentMapBuilder persistentMapBuilder() { return new DefaultPersistentMapBuilder<>(localDB); } public PersistentSetBuilder persistentSetBuilder() { return new DefaultPersistentSetBuilder<>(localDB); } private class CommitTask extends TimerTask { @Override public void run() { localDB.commit(); } } }