package org.araqne.logstorage.engine; import java.io.*; import java.util.HashSet; 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.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.Requires; import org.apache.felix.ipojo.annotations.Validate; import org.araqne.confdb.ConfigService; import org.araqne.logstorage.LogFileService; import org.araqne.logstorage.LogFileServiceEventListener; import org.araqne.logstorage.LogFileServiceRegistry; import org.araqne.logstorage.file.LogFileReader; import org.araqne.logstorage.file.LogFileWriter; import org.araqne.logstorage.repair.IntegrityChecker; import org.araqne.storage.api.StorageManager; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Component(name = "logstorage-log-file-service-registry") @Provides public class LogFileServiceRegistryImpl implements LogFileServiceRegistry { private final static Logger logger = LoggerFactory.getLogger(LogFileServiceRegistryImpl.class); /** * force dependency */ @Requires private IntegrityChecker consistencyChecker; @Requires private ConfigService conf; @Requires private StorageManager storageManager; private BundleContext bc; private ConcurrentHashMap<String, WaitEvent> availableEngines = new ConcurrentHashMap<String, WaitEvent>(); private ConcurrentMap<String, LogFileService> services = new ConcurrentHashMap<String, LogFileService>(); private Set<LogFileServiceEventListener> listeners = new CopyOnWriteArraySet<LogFileServiceEventListener>(); public LogFileServiceRegistryImpl(BundleContext bc) { this.bc = bc; } @Validate public void start() throws IOException { loadEngineList(); } private void loadEngineList() throws FileNotFoundException, UnsupportedEncodingException, IOException { File f = getEngineListFile(); if (!f.exists()) { availableEngines.put("v2", new WaitEvent("v2")); 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(); availableEngines.putIfAbsent(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 getEngineListFile() { return bc.getDataFile("engine.lst"); } @Override public LogFileWriter newWriter(String type, Map<String, Object> options) { WaitEvent ev = availableEngines.get(type); if (ev == null) throw new UnsupportedOperationException("not supported engine: " + type); ev.await(); LogFileService logFileService = services.get(type); LogFileWriter newWriter = logFileService.newWriter(options); return newWriter; } @Override public LogFileReader newReader(String tableName, String type, Map<String, Object> options) { WaitEvent ev = availableEngines.get(type); if (ev == null) throw new UnsupportedOperationException("not supported engine: " + type); ev.await(); LogFileService logFileService = services.get(type); return logFileService.newReader(tableName, options); } @Override public void register(LogFileService service) { String type = service.getType(); logger.info("araqne logstorage: loaded file engine [{}]", type); services.put(type, service); WaitEvent ev = new WaitEvent(type, true); WaitEvent old = availableEngines.putIfAbsent(type, ev); if (old != null) { old.setReady(); } else { rewriteEngineListFile(); } } private void rewriteEngineListFile() { // update engine list file FileOutputStream fos = null; BufferedWriter bw = null; try { File f = getEngineListFile(); f.delete(); fos = new FileOutputStream(f); bw = new BufferedWriter(new OutputStreamWriter(fos, "utf-8")); for (String name : availableEngines.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) { } } } } @Override public void unregister(LogFileService service) { String type = service.getType(); WaitEvent ev = availableEngines.get(type); if (ev == null) throw new UnsupportedOperationException("not supported engine: " + type); for (LogFileServiceEventListener l : listeners) { try { l.onUnloadingFileService(type); } catch (Throwable t) { logger.warn("exception caught", t); } } ev.ready = false; services.remove(type); logger.info("araqne logstorage: unloaded file engine [{}]", type); } @Override public String[] getServiceTypes() { return services.keySet().toArray(new String[0]); } @Override public LogFileService getLogFileService(String type) { WaitEvent ev = availableEngines.get(type); if (ev == null) throw new UnsupportedOperationException("not supported engine: " + type); ev.await(); return services.get(type); } @Override public String[] getInstalledTypes() { Set<String> types = new HashSet<String>(availableEngines.keySet()); types.add("v2"); return types.toArray(new String[0]); } @Override public void uninstall(String type) { WaitEvent ev = availableEngines.remove(type); if (ev == null) throw new IllegalStateException("not installed engine: " + type); rewriteEngineListFile(); } 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.debug("araqne logstorage: waiting file engine [{}]", type); cond.await(); } catch (InterruptedException e) { } } } finally { lock.unlock(); } logger.debug("araqne logstorage: file engine [{}] ready!", type); } public void setReady() { ready = true; lock.lock(); try { cond.signalAll(); } finally { lock.unlock(); } } } @Override public void addListener(LogFileServiceEventListener listener) { listeners.add(listener); } @Override public void removeListener(LogFileServiceEventListener listener) { listeners.remove(listener); } }