package org.araqne.logstorage.repair;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Provides;
import org.apache.felix.ipojo.annotations.Validate;
import org.araqne.logstorage.LogFileRepairer;
import org.araqne.logstorage.LogFileRepairerRegistry;
import org.araqne.logstorage.LogFileRepairerService;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(name = "logstorage-log-file-repairer-registry")
@Provides
public class LogFileRepairerRegistryImpl implements LogFileRepairerRegistry {
private final static Logger logger = LoggerFactory.getLogger(LogFileRepairerRegistryImpl.class);
private BundleContext bc;
private ConcurrentHashMap<String, WaitEvent> availableRepairers = new ConcurrentHashMap<String, WaitEvent>();
private ConcurrentMap<String, LogFileRepairerService> repairers = new ConcurrentHashMap<String, LogFileRepairerService>();
public LogFileRepairerRegistryImpl(BundleContext bc) {
this.bc = bc;
String opt = System.getProperty("araqne.logstorage.repair");
if (opt != null && (opt.equalsIgnoreCase("disabled") || !Boolean.parseBoolean(opt)))
bc.registerService(IntegrityChecker.class.getName(), new DummyIntegrityChecker(), null);
}
@Override
public void register(LogFileRepairerService repairer) {
String type = repairer.getType();
logger.info("araqne logstorage: loaded file repairer [{}]", type);
repairers.put(type, repairer);
WaitEvent ev = new WaitEvent(type, true);
WaitEvent old = availableRepairers.putIfAbsent(type, ev);
if (old != null) {
old.setReady();
} else {
rewriteRepairerListFile();
}
}
@Override
public void unregister(LogFileRepairerService repairer) {
String type = repairer.getType();
WaitEvent ev = availableRepairers.get(type);
if (ev == null)
throw new UnsupportedOperationException("not supported repairer: " + type);
ev.ready = false;
repairers.remove(type);
logger.info("araqne logstorage: unloaded file repairer [{}]", type);
}
@Override
public void uninstall(String type) {
WaitEvent ev = availableRepairers.remove(type);
if (ev == null)
throw new IllegalStateException("not installed repairer: " + type);
rewriteRepairerListFile();
}
@Override
public String[] getRepairerTypes() {
return availableRepairers.keySet().toArray(new String[0]);
}
@Override
public String[] getInstalledTypes() {
return availableRepairers.keySet().toArray(new String[0]);
}
@Override
public LogFileRepairer newRepairer(String type) {
WaitEvent ev = availableRepairers.get(type);
if (ev == null)
throw new UnsupportedOperationException("not supported repairer: " + type);
ev.await();
LogFileRepairerService logFileRepairerService = repairers.get(type);
return logFileRepairerService.newRepairer();
}
@Validate
public void start() throws IOException {
File f = getRepairerListFile();
if (!f.exists())
return;
// load file engine list
FileInputStream is = null;
BufferedReader br = null;
try {
is = new FileInputStream(f);
br = new BufferedReader(new InputStreamReader(is, "utf-8"));
while (true) {
String line = br.readLine();
if (line == null)
break;
String type = line.trim();
availableRepairers.put(type, new WaitEvent(type));
}
} finally {
ensureClose(br);
ensureClose(is);
}
}
private void ensureClose(Closeable c) {
if (c != null) {
try {
c.close();
} catch (IOException e) {
}
}
}
private File getRepairerListFile() {
return bc.getDataFile("repairer.lst");
}
private void rewriteRepairerListFile() {
// update engine list file
FileOutputStream fos = null;
BufferedWriter bw = null;
try {
File f = getRepairerListFile();
f.delete();
fos = new FileOutputStream(f);
bw = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"));
for (String name : availableRepairers.keySet())
bw.write(name + "\n");
} catch (IOException e) {
logger.error("araqne logstorage: cannot update engine list", e);
} finally {
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
}
}
}
}
// XXX : copy from LogFileServiceRegistryImpl
private static class WaitEvent {
private String type;
private Lock lock = new ReentrantLock();
private Condition cond = lock.newCondition();
private volatile boolean ready;
public WaitEvent(String type) {
this(type, false);
}
public WaitEvent(String type, boolean ready) {
this.type = type;
this.ready = ready;
}
public void await() {
if (ready)
return;
lock.lock();
try {
while (!ready) {
try {
logger.info("araqne logstorage: waiting file engine [{}]", type);
cond.await();
} catch (InterruptedException e) {
}
}
} finally {
lock.unlock();
}
logger.info("araqne logstorage: file engine [{}] ready!", type);
}
public void setReady() {
ready = true;
lock.lock();
try {
cond.signalAll();
} finally {
lock.unlock();
}
}
}
private class DummyIntegrityChecker implements IntegrityChecker {
}
}