package com.fourspaces.featherdb.backend.inmemory; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import com.fourspaces.featherdb.FeatherDB; import com.fourspaces.featherdb.auth.Credentials; import com.fourspaces.featherdb.backend.Backend; import com.fourspaces.featherdb.backend.BackendException; import com.fourspaces.featherdb.document.Document; import com.fourspaces.featherdb.document.DocumentCreationException; /** * Stores documents / databases in memory in a series of ConcurrentHashMaps. The use of ConcurrentHashMaps * makes this backend provider thread-safe. * * @author mbreese * */ public class InMemoryBackend implements Backend { final private Map<String,InMemoryDB> dbs = new ConcurrentHashMap<String,InMemoryDB>(); private String filename="feather.db"; private FeatherDB featherDB; public InMemoryBackend() { } public InMemoryBackend(String filename) { this.filename = filename; } public void deleteDocument(String db, String id){ dbs.get(db).remove(id); } @SuppressWarnings("unchecked") public void init(FeatherDB featherDB) { this.featherDB=featherDB; String configFilename = featherDB.getProperty("backend.inmemory.filename"); if (configFilename != null) { filename = configFilename; } if (filename !=null) { File f = new File(filename); if (f!=null && f.exists()) { System.out.println("loading inmemory hashmap from file: "+filename); ObjectInputStream ois=null; try { FileInputStream fis = new FileInputStream(f); ois = new ObjectInputStream(fis); Map<String, InMemoryDB> map = (Map<String, InMemoryDB>) ois.readObject(); dbs.putAll(map); } catch (FileNotFoundException e) { e.printStackTrace(); throw new RuntimeException(e); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } catch (ClassNotFoundException e) { e.printStackTrace(); throw new RuntimeException(e); } finally { if (ois!=null) { try { ois.close(); } catch (IOException e) { } } } } } } public void shutdown() { if (filename !=null) { File f = new File(filename); if (f==null) { try { f.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } System.out.println("saving featherDB to file: "+filename); ObjectOutputStream oos=null; try { FileOutputStream fos = new FileOutputStream(f); oos = new ObjectOutputStream(fos); oos.writeObject(dbs); } catch (FileNotFoundException e) { e.printStackTrace(); throw new RuntimeException(e); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } finally { if (oos!=null) { try { oos.close(); } catch (IOException e) { } } } } } public Iterable<Document> allDocuments(final String db) { return getDocuments(db,null); } public Iterable<Document> getDocuments(final String db, final String[] ids) { if (!dbs.containsKey(db)) { return null; } final Collection<InMemoryDocument> list; if (ids== null) { list = dbs.get(db).getAllDocuments(); } else { list = new ArrayList<InMemoryDocument>(); for (String id: ids) { list.add(dbs.get(db).get(id)); } } final Iterator<Document> i = new Iterator<Document> () { int index = 0; Document nextDoc = null; Iterator<InMemoryDocument> iterator = list.iterator(); private void findNext() { nextDoc=null; while (iterator.hasNext() && nextDoc == null) { InMemoryDocument imdoc = iterator.next(); index++; nextDoc = getDocument(db,imdoc.getId()); } } public boolean hasNext() { if (index==0 && nextDoc==null) { findNext(); } return nextDoc!=null; } public Document next() { Document obj=nextDoc; findNext(); return obj; } public void remove() { findNext(); } }; return new Iterable<Document>() { public Iterator<Document> iterator() { return i; }}; } public Set<String> getDatabaseNames(Credentials credentials) { return dbs.keySet(); } public Document getDocument(String db, String id){ if (!dbs.containsKey(db)) { return null; } return getDocument(db,id,null); } public Document getDocument(String db, String id, String rev) { if (!dbs.containsKey(db)) { return null; } if (dbs.get(db).get(id) == null) { return null; } try { return dbs.get(db).get(id).getRevision(rev); } catch (DocumentCreationException e) { e.printStackTrace(); } return null; } public void addDatabase(String name) throws BackendException{ if (!dbs.containsKey(name)) { dbs.put(name, new InMemoryDB(name)); } else { throw new BackendException("The database '"+name+"' already exists"); } } public void deleteDatabase(String name) throws BackendException{ if (!dbs.containsKey(name)) { throw new BackendException("The database '"+name+"' was not found"); } dbs.remove(name); } public boolean doesDatabaseExist(String db) { return dbs.containsKey(db); } public boolean doesDocumentExist(String db, String id) { if (dbs.get(db)==null) { return false; } return dbs.get(db).get(id)!=null; } public boolean doesDocumentRevisionExist(String db, String id, String revision) { if (dbs.get(db)==null) { return false; } if (dbs.get(db).get(id)==null) { return false; } try { return dbs.get(db).get(id).getRevision(revision)!=null; } catch (DocumentCreationException e) { } return false; } public Document saveDocument(Document doc) { dbs.get(doc.getDatabase()).save(doc); featherDB.recalculateViewForDocument(doc); return doc; } public JSONArray getDocumentRevisions(String db, String id) { if (!dbs.containsKey(db)) { return null; } JSONArray ar = new JSONArray(); InMemoryDocument imdoc = dbs.get(db).get(id); for (String rev: imdoc.getRevisions()) { JSONObject o = new JSONObject(); try { o.put("_id", imdoc.getId()); o.put("_rev", rev); } catch (JSONException e) { e.printStackTrace(); } ar.put(o); } return ar; } public Set<String> getDatabaseNames() { return dbs.keySet(); } public Map<String, Object> getDatabaseStats(String name) { Map<String, Object> m = new HashMap<String,Object>(); m.put("db_name", name); m.put("doc_count", dbs.get(name).size()); return m; } public void touchRevision(String db, String id, String rev) { dbs.get(db).get(id).touchRevision(rev); } }