/*
* Copyright 2010 NCHOVY
*
* 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.logstorage.engine;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Provides;
import org.apache.felix.ipojo.annotations.Requires;
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.ConfigTransaction;
import org.krakenapps.confdb.Predicates;
import org.krakenapps.logstorage.LogTableEventListener;
import org.krakenapps.logstorage.LogTableNotFoundException;
import org.krakenapps.logstorage.LogTableRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(name = "logstorage-table-registry")
@Provides
public class LogTableRegistryImpl implements LogTableRegistry {
private final Logger logger = LoggerFactory.getLogger(LogTableRegistryImpl.class.getName());
@Requires
private ConfigService conf;
/**
* table id generator
*/
private AtomicInteger nextTableId;
/**
* table id to name mappings
*/
private ConcurrentMap<Integer, String> tableNames;
/**
* table name to schema mappings
*/
private ConcurrentMap<String, LogTableSchema> tableSchemas;
private CopyOnWriteArraySet<LogTableEventListener> callbacks;
public LogTableRegistryImpl() {
tableSchemas = new ConcurrentHashMap<String, LogTableSchema>();
tableNames = new ConcurrentHashMap<Integer, String>();
callbacks = new CopyOnWriteArraySet<LogTableEventListener>();
// load table id mappings
loadTableMappings();
}
private void loadTableMappings() {
int maxId = 0;
ConfigDatabase db = conf.ensureDatabase("kraken-logstorage");
ConfigCollection col = db.getCollection(LogTableSchema.class);
if (col == null) {
col = db.ensureCollection(LogTableSchema.class);
ConfigCollection before = db.getCollection("org.krakenapps.logstorage.engine.LogTableSchema");
if (before != null) {
ConfigTransaction xact = db.beginTransaction();
try {
ConfigIterator it = before.findAll();
while (it.hasNext())
col.add(xact, it.next().getDocument());
xact.commit("kraken-logstorage", "migration from collection org.krakenapps.logstorage.engine.LogTableSchema");
db.dropCollection("org.krakenapps.logstorage.engine.LogTableSchema");
} catch (Throwable e) {
xact.rollback();
throw new IllegalStateException("migration failed");
}
}
}
ConfigIterator it = col.findAll();
for (LogTableSchema t : it.getDocuments(LogTableSchema.class)) {
tableNames.put(t.getId(), t.getName());
tableSchemas.put(t.getName(), t);
if (maxId < t.getId())
maxId = t.getId();
}
nextTableId = new AtomicInteger(maxId);
}
@Override
public boolean exists(String tableName) {
return tableSchemas.containsKey(tableName);
}
@Override
public Collection<String> getTableNames() {
return new ArrayList<String>(tableSchemas.keySet());
}
@Override
public int getTableId(String tableName) {
LogTableSchema table = tableSchemas.get(tableName);
if (table == null)
throw new LogTableNotFoundException(tableName);
return table.getId();
}
@Override
public String getTableName(int tableId) {
return tableNames.get(tableId);
}
@Override
public void createTable(String tableName, Map<String, String> tableMetadata) {
if (tableSchemas.containsKey(tableName))
throw new IllegalStateException("table already exists: " + tableName);
int newId = nextTableId.incrementAndGet();
LogTableSchema table = new LogTableSchema(newId, tableName);
if (tableMetadata != null)
table.getMetadata().putAll(tableMetadata);
ConfigDatabase db = conf.ensureDatabase("kraken-logstorage");
db.add(table, "kraken-logstorage", "created " + tableName + " table");
tableNames.put(table.getId(), table.getName());
tableSchemas.put(tableName, table);
// invoke callbacks
for (LogTableEventListener callback : callbacks) {
try {
callback.onCreate(tableName, tableMetadata);
} catch (Exception e) {
logger.warn("kraken logstorage: table event listener should not throw any exception", e);
}
}
}
@Override
public void renameTable(String currentName, String newName) {
// check duplicated name first
if (tableSchemas.containsKey(newName))
throw new IllegalStateException("table already exists: " + newName);
// change renamed table metadata
ConfigDatabase db = conf.ensureDatabase("kraken-logstorage");
Config c = db.findOne(LogTableSchema.class, Predicates.field("name", currentName));
if (c == null)
throw new IllegalStateException("table not found: " + currentName);
LogTableSchema schema = c.getDocument(LogTableSchema.class);
schema.setName(newName);
db.update(c, schema);
// rename table in memory
tableSchemas.remove(currentName);
tableSchemas.putIfAbsent(newName, schema);
tableNames.put(schema.getId(), newName);
}
@Override
public void dropTable(String tableName) {
LogTableSchema old = tableSchemas.remove(tableName);
if (old == null)
throw new IllegalStateException("table not found: " + tableName);
ConfigDatabase db = conf.ensureDatabase("kraken-logstorage");
Config c = db.findOne(LogTableSchema.class, Predicates.field("name", tableName));
if (c == null)
return;
db.remove(c);
LogTableSchema t = tableSchemas.remove(tableName);
if (t != null)
tableNames.remove(t.getId());
// invoke callbacks
for (LogTableEventListener callback : callbacks) {
try {
callback.onDrop(tableName);
} catch (Exception e) {
logger.warn("kraken logstorage: table event listener should not throw any exception", e);
}
}
}
@Deprecated
@Override
public org.krakenapps.logstorage.TableMetadata getTableMetadata(int tableId) {
String tableName = tableNames.get(tableId);
if (tableName == null)
return null;
LogTableSchema t = tableSchemas.get(tableName);
Map<String, String> m = new HashMap<String, String>();
for (String key : t.getMetadata().keySet()) {
Object value = t.getMetadata().get(key);
m.put(key, value == null ? null : value.toString());
}
return new org.krakenapps.logstorage.TableMetadata(tableId, tableName, m);
}
@Override
public Set<String> getTableMetadataKeys(String tableName) {
LogTableSchema t = tableSchemas.get(tableName);
if (t == null)
throw new IllegalArgumentException("table not exists: " + tableName);
return t.getMetadata().keySet();
}
@Override
public String getTableMetadata(String tableName, String key) {
LogTableSchema t = tableSchemas.get(tableName);
if (t == null)
throw new IllegalArgumentException("table not exists: " + tableName);
return (String) t.getMetadata().get(key);
}
@Override
public void setTableMetadata(String tableName, String key, String value) {
LogTableSchema t = tableSchemas.get(tableName);
if (t == null)
throw new IllegalArgumentException("table not exists: " + tableName);
ConfigDatabase db = conf.ensureDatabase("kraken-logstorage");
Config c = db.findOne(LogTableSchema.class, Predicates.field("name", tableName));
t.getMetadata().put(key, value);
db.update(c, t, false, "kraken-logstorage", "set table [" + tableName + "] metadata " + key + " to " + value);
}
@Override
public void unsetTableMetadata(String tableName, String key) {
LogTableSchema t = tableSchemas.get(tableName);
if (t == null)
throw new IllegalArgumentException("table not exists: " + tableName);
ConfigDatabase db = conf.ensureDatabase("kraken-logstorage");
Config c = db.findOne(LogTableSchema.class, Predicates.field("name", tableName));
t.getMetadata().remove(key);
db.update(c, t, false, "kraken-logstorage", "unset table [" + tableName + "] metadata " + key);
}
@Override
public void addListener(LogTableEventListener listener) {
callbacks.add(listener);
}
@Override
public void removeListener(LogTableEventListener listener) {
callbacks.remove(listener);
}
}