package com.fourspaces.featherdb.views;
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.HashMap;
import java.util.Map;
import org.json.JSONObject;
import com.fourspaces.featherdb.FeatherDB;
import com.fourspaces.featherdb.document.Document;
import com.fourspaces.featherdb.document.JSONDocument;
import com.fourspaces.featherdb.utils.Logger;
/**
* This view manager is very simple... it reruns each view upon request (basically adhoc style)
* It also serializes each Java view object (named /basedir/_view_name/function_name/view.obj),
* which is how it persists whether or not a view exists...
*
*/
public class SimpleViewManager extends ViewManager {
protected FeatherDB featherDB;
protected Logger log = Logger.get(SimpleViewManager.class);
protected File baseDir;
protected Map<String,View> views = new HashMap<String,View>();
protected final static String VIEW_INSTANCE_NAME = "view.obj";
public SimpleViewManager(){
}
/* (non-Javadoc)
* @see com.fourspaces.featherdb.views.ViewManager#init()
*/
public void init(FeatherDB featherDB) throws ViewException {
this.featherDB=featherDB;
baseDir = new File(featherDB.getProperty("view.simple.path"));
if (baseDir==null) {
throw new RuntimeException("Could not open SimpleViewManager path (view.simple.dir)");
}
if (!baseDir.exists()) {
baseDir.mkdirs();
}
//Document d = featherDB.getBackend().getDocument("_views", "list");
for (String db : featherDB.getBackend().getDatabaseNames()) {
log.debug("Loading views for: {}", db);
loadViewsForDatabase(db);
}
}
/* (non-Javadoc)
* @see com.fourspaces.featherdb.views.ViewManager#shutdown()
*/
public void shutdown() {
}
protected File viewDbDir(String db) {
return new File(baseDir,db);
}
protected File viewDir(String db, String viewName) {
return new File(viewDbDir(db),viewName);
}
protected void loadViewsForDatabase(String db) throws ViewException {
File viewDbDir = viewDbDir(db);
if (!viewDbDir.exists()) {
initDatabaseViews(db);
} else {
for(File instanceDir:viewDbDir.listFiles()) {
if (instanceDir.isDirectory()) {
for (File functionDir: instanceDir.listFiles()) {
ObjectInputStream ois =null;
try {
ois = new ObjectInputStream(new FileInputStream(new File(functionDir,VIEW_INSTANCE_NAME)));
log.debug("Loading view {}/{}/{} ",db,instanceDir.getName(),functionDir.getName());
views.put(db+"/"+instanceDir.getName()+"/"+functionDir.getName(),(View) ois.readObject());
} catch (FileNotFoundException e) {
throw new ViewException(e);
} catch (IOException e) {
throw new ViewException(e);
} catch (ClassNotFoundException e) {
throw new ViewException(e);
} finally {
if (ois!=null) {
try {
ois.close();
} catch (IOException e) {
}
}
}
}
}
}
}
}
/* (non-Javadoc)
* @see com.fourspaces.featherdb.views.ViewManager#addView(com.fourspaces.featherdb.document.Document)
*/
public void addView(JSONDocument jsondoc) throws ViewException {
String viewType = (String) jsondoc.get("view_type");
if (viewType == null || viewType.equals("text/javascript")) {
for (String key:jsondoc.keys()) {
if (((String)jsondoc.get(key)).startsWith("function")) {
log.debug("Adding javascript view: {}/{}/{} => {}",jsondoc.getDatabase(),jsondoc.getId(),key,jsondoc.get(key));
addView(jsondoc.getDatabase(),jsondoc.getId(),key,new JavaScriptView(jsondoc.getDatabase(),(String) jsondoc.get(key)));
}
}
} else if (viewType.startsWith("java:")){
log.debug("Adding java view: {}/{} => {}", jsondoc.getDatabase(),jsondoc.getId(),viewType);
try {
Class clazz = Thread.currentThread().getContextClassLoader().loadClass(viewType.substring(5));
addView(jsondoc.getDatabase(),jsondoc.getId(),DEFAULT_FUNCTION_NAME,(View) clazz.newInstance());
} catch (ClassNotFoundException e) {
throw new ViewException(e);
} catch (ViewException e) {
throw new ViewException(e);
} catch (InstantiationException e) {
throw new ViewException(e);
} catch (IllegalAccessException e) {
throw new ViewException(e);
}
} else {
log.warn("Don't know how to handle view type: {}\n{}", viewType,jsondoc.toString());
}
}
protected View getView(String db, String view, String function) {
return views.get(db+"/"+view+"/"+function);
}
public JSONObject getViewResults(String db, String viewName, String function) {
return AdHocViewRunner.runView(featherDB,db,viewName,function,getView(db,viewName,function));
}
public void recalculateDocument(Document doc) {
// this manager recalculates all views on the fly... so this isn't needed.
// but we still need to add new views!
if (doc.getId().startsWith("_") && doc instanceof JSONDocument) {
try {
addView((JSONDocument) doc);
} catch (ViewException e) {
log.error("Error adding new view: {}",doc.getId(),e);
}
}
}
public void initDatabaseViews(String db) throws ViewException {
File viewDir = viewDbDir(db);
viewDir.mkdirs();
addView(db,"_all_docs",DEFAULT_FUNCTION_NAME,new AllDocuments(db));
}
public void addView(String db, String view, String function,View instance) throws ViewException {
File viewDir = new File(viewDir(db,view),function);
if (!viewDir.exists()) {
viewDir.mkdirs();
}
ObjectOutputStream oos =null;
try {
oos = new ObjectOutputStream(new FileOutputStream(new File(viewDir,VIEW_INSTANCE_NAME)));
oos.writeObject(instance);
oos.close();
} catch (FileNotFoundException e) {
throw new ViewException(e);
} catch (IOException e) {
throw new ViewException(e);
} finally {
if (oos!=null) {
try {
oos.close();
} catch (IOException e) {
}
}
}
views.put(db+"/"+view+"/"+function,instance);
}
public void removeDatabaseViews(String db) {
recursivelyDeleteFiles(viewDbDir(db));
}
private void recursivelyDeleteFiles(File file) {
if (file.isDirectory()) {
for (File f:file.listFiles()) {
recursivelyDeleteFiles(f);
}
}
file.delete();
}
public boolean doesViewExist(String db, String view, String function) {
return views.containsKey(db+"/"+view+"/"+function);
}
public static JSONObject adHocView(FeatherDB featherDB,String db,String viewName, String functionName, String src) throws ViewException {
View view=new JavaScriptView(db,src);
return adHocView(featherDB,db,viewName,functionName,view);
}
public static JSONObject adHocView(FeatherDB featherDB,String db,String viewName, String functionName,View view) {
view.setBackend(featherDB.getBackend());
JSONObject results = new JSONObject();
int total = 0;
for (Document doc: featherDB.getBackend().allDocuments(db)) {
JSONObject result = view.filter(doc);
if (result!=null) {
results.put((String) result.get("key"),result.get("value"));
total++;
}
}
JSONObject out = new JSONObject();
out.put("view", viewName+"/"+functionName);
out.put("total_rows", total);
out.put("rows",results);
return out;
}
}