/* * Copyright 2011 Future Systems, Inc. * * 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.krakenapps.confdb.file; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArraySet; import org.krakenapps.api.PrimitiveConverter; import org.krakenapps.confdb.ConfigServiceListener; import org.krakenapps.confdb.Config; import org.krakenapps.confdb.ConfigCollection; import org.krakenapps.confdb.ConfigDatabase; import org.krakenapps.confdb.ConfigIterator; import org.krakenapps.confdb.ConfigService; import org.krakenapps.confdb.Predicates; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FileConfigService implements ConfigService { private final Logger logger = LoggerFactory.getLogger(FileConfigService.class.getName()); private File baseDir; private ConcurrentMap<DatabaseCacheKey, ConfigDatabase> instances; private ConfigDatabase metadb; private CopyOnWriteArraySet<ConfigServiceListener> listeners; public FileConfigService() throws IOException { listeners = new CopyOnWriteArraySet<ConfigServiceListener>(); baseDir = new File(System.getProperty("kraken.data.dir"), "kraken-confdb"); baseDir.mkdirs(); metadb = new FileConfigDatabase(baseDir, "confdb"); instances = new ConcurrentHashMap<DatabaseCacheKey, ConfigDatabase>(); } @Override public List<String> getDatabaseNames() { ConfigCollection col = metadb.ensureCollection("database"); ConfigIterator it = col.findAll(); List<String> names = new ArrayList<String>(); while (it.hasNext()) { Config next = it.next(); DatabaseMetadata meta = PrimitiveConverter.parse(DatabaseMetadata.class, next.getDocument()); names.add(meta.name); } return names; } @Override public ConfigDatabase getDatabase(String name) { return getDatabase(name, null); } @Override public ConfigDatabase getDatabase(String name, Integer rev) { try { DatabaseCacheKey cacheKey = new DatabaseCacheKey(name, rev); ConfigDatabase db = instances.get(cacheKey); if (db != null) return db; ConfigCollection col = metadb.ensureCollection("database"); Config c = col.findOne(Predicates.field("name", name)); if (c == null) return null; db = new FileConfigDatabase(baseDir, name, rev); ConfigDatabase old = instances.putIfAbsent(cacheKey, db); return old != null ? old : db; } catch (IOException e) { throw new RuntimeException(e); } } @Override public ConfigDatabase ensureDatabase(String name) { ConfigDatabase db = getDatabase(name); if (db == null) createDatabase(name); return getDatabase(name); } @Override public ConfigDatabase createDatabase(String name) { try { ConfigCollection col = metadb.ensureCollection("database"); Config c = col.findOne(Predicates.field("name", name)); if (c != null) throw new IllegalStateException("db already exists: " + name); DatabaseMetadata meta = new DatabaseMetadata(name); col.add(PrimitiveConverter.serialize(meta)); ConfigDatabase db = new FileConfigDatabase(baseDir, name); instances.putIfAbsent(new DatabaseCacheKey(name, null), db); for (ConfigServiceListener listener : listeners) { try { listener.onCreateDatabase(db); } catch (Throwable t) { logger.error("kraken confdb: create config database callback should not throw any exception", t); } } return db; } catch (IOException e) { throw new RuntimeException(e); } } @Override public void dropDatabase(String name) { try { ConfigCollection col = metadb.ensureCollection("database"); Config c = col.findOne(Predicates.field("name", name)); if (c == null) throw new IllegalStateException("db not exists"); col.remove(c); FileConfigDatabase db = (FileConfigDatabase) instances.remove(new DatabaseCacheKey(name, null)); if (db == null) db = new FileConfigDatabase(baseDir, name); db.purge(); } catch (IOException e) { throw new RuntimeException(e); } } @SuppressWarnings("unused") private static class DatabaseMetadata { private String name; private Date created = new Date(); public DatabaseMetadata() { } public DatabaseMetadata(String name) { this.name = name; } } private static class DatabaseCacheKey { private String name; private Integer rev; public DatabaseCacheKey(String name, Integer rev) { this.name = name; this.rev = rev; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((rev == null) ? 0 : rev.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; DatabaseCacheKey other = (DatabaseCacheKey) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (rev == null) { if (other.rev != null) return false; } else if (!rev.equals(other.rev)) return false; return true; } } @Override public void addListener(ConfigServiceListener listener) { if (listener == null) throw new IllegalArgumentException("listener should not be null"); listeners.add(listener); } @Override public void removeListener(ConfigServiceListener listener) { if (listener == null) throw new IllegalArgumentException("listener should not be null"); listeners.remove(listener); } }