/* * Copyright 2015-present 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.osgi.service.component.ComponentContext; import org.slf4j.Logger; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import static org.onosproject.security.AppGuard.checkPermission; import static org.onosproject.security.AppPermission.Type.PERSISTENCE_WRITE; 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 = "localDB"; 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 Timer timer; private final CommitTask commitTask = new CommitTask(); @Activate public void activate(ComponentContext context) { timer = new Timer(); // bundle's persistent storage area directory File dbFolderPath = context.getBundleContext().getDataFile(""); Path dbPath = dbFolderPath.toPath().resolve(DATABASE_PATH); log.debug("dbPath: {}", dbPath); //Make sure the directory exists, if it does not, make it. if (!dbFolderPath.isDirectory()) { log.info("The specified folder location for the database did not exist and will be created."); try { Files.createDirectories(dbFolderPath.toPath()); } 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() { timer.cancel(); for (Map.Entry<String, Object> 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"); } @Override public <K, V> PersistentMapBuilder<K, V> persistentMapBuilder() { checkPermission(PERSISTENCE_WRITE); return new DefaultPersistentMapBuilder<>(localDB); } @Override public <E> PersistentSetBuilder<E> persistentSetBuilder() { checkPermission(PERSISTENCE_WRITE); return new DefaultPersistentSetBuilder<>(localDB); } private class CommitTask extends TimerTask { @Override public void run() { localDB.commit(); } } }