sumeru
Version:
A Realtime Javascript RIA Framework For Mobile WebApp
510 lines (390 loc) • 10.6 kB
JavaScript
var fw = fw || {};
fw = require('../sumeru/pkg.js')(fw);
//var Model = require('../sumeru/model.js')(fw);
//var Collection = require('../sumeru/collection.js')(fw);
//var DAL = require('../sumeru/dal.js');
/**
* mock for mongodb collection
*/
var randomGen;
(function(){
var _private = fw._private || {};
var __randomMap = {};
randomGen = function(len) {
len = len || 10;
var chars = "qwertyuiopasdfghjklzxcvbnm1234567890",
charsLen = chars.length,
len2 = len,
rand = "";
while (len2--) {
rand += chars.charAt(Math.floor(Math.random() * charsLen));
}
if (__randomMap[rand]) {
return random(len);
}
__randomMap[rand] = 1;
return rand;
};
})();
var MockCollection = function(){
this.modelsContainer = [];
};
MockCollection.prototype = {
find : function(whereMap){
var rs = [],
_whereMap = {};
if(typeof whereMap == 'undefined'){
} else if(arguments.length == 2){ //如果传入了两个参数,则为key, val
_whereMap[arguments[0]] = arguments[1];
} else if(Object.prototype.toString.call(whereMap) == '[object Object]'){ //否则就是包含多个key value的object
_whereMap = whereMap;
}
var val,
item,
valStr,
isQualify;
for(var i = 0, l = this.modelsContainer.length; i < l; i++){
item = this.modelsContainer[i];
isQualify = true;
for (var field in _whereMap){
val = new RegExp(_whereMap[field]);
valStr = item[field] + '';
if(!valStr.match(val)){
isQualify = false;
break;
}
}
if(isQualify == true){
rs.push(item);
}
}
return rs;
},
save : function(toSave){
var data = JSON.parse(JSON.stringify(toSave));
data._id = randomGen(10);
this.modelsContainer.push(data);
},
remove : function(where){
var rs = this.find.apply(this, arguments);
for (var i = 0, l = rs.length; i < l; i++){
for (var j = 0, k = this.modelsContainer.length; j < k; j++){
if(rs[i] == this.modelsContainer[j]){
this.modelsContainer.splice(j, 1);
j--;
k--;
continue;
}
}
}
},
update : function(where, data){
var rs = this.find(where),
toSave = JSON.parse(JSON.stringify(data));
console.log(rs, toSave);
for (var i = 0, l = rs.length; i < l; i++){
for(var key in toSave['$set']){
if(key == '_id') continue;
rs[i][key] = toSave['$set'][key];
}
}
}
};
//var mongodb = require('mongodb');
//var ObjectId = mongodb.ObjectID;
//var server = new mongodb.Server("127.0.0.1", 27017, {});
//var db = new mongodb.Db('test', server, {});
var messageSend;
var SocketMgr = {};
/**
* SubscribeMgr = {
* @modelname : [
* {
* socketId : xxx,
* pubname : xxx,
* args : [arg1, arg2...]
* }
* ...
* ]
* }
*/
var SubscribeMgr = {};
var PublishContainer = {};
/*db.open(function(err, db){
runStub(db);
});*/
var runStub = function(db){
//startup a server
var PORT = 2012;
var http = require("http"),
path = require('path'),
fs = require('fs'),
sockjs = require("sockjs");
//start file server
var fileServer = http.createServer(function(req, res){
var localBase = '..',
filePath = req.url;
if(filePath == './'){
filePath = localBase + '/index.html';
} else {
filePath = localBase + filePath;
}
//把问号后面去掉
if(filePath.indexOf('?') != -1){
filePath = filePath.split('?')[0];
}
console.log('file server accessing ' + filePath);
var extensionName = path.extname(filePath),
contentType = 'text/html';
extMap = {
'.js' : 'text/javascript',
'.css' : 'text/css',
'json' : 'text/json'
};
if(extMap[extensionName]){
contentType = extMap[extensionName];
}
path.exists(filePath, function(exists){
if(exists){
fs.readFile(filePath, function(error, content){
if(error){
res.writeHead(500);
res.end();
} else {
res.writeHead(200, {'Content-Type' : contentType});
res.end(content, 'utf-8');
}
});
} else {
res.writeHead(404);
res.end();
}
});
});
var FILE_PORT = 80;
fileServer.listen(FILE_PORT, function(){
console.log('File Server Listening on ' + FILE_PORT);
});
//start websocket server
var globalServer = http.createServer(function(req, res){
}).listen(PORT, function(){
console.log('Server Listening on ' + PORT);
});
//start websocket server
var sock = sockjs.createServer();
sock.installHandlers(globalServer, {
prefix: '/socket'
});
sock.on("connection", function(conn){
conn.on("data", function(msg){
//FIXME 做跨域连接检测和授权检查
serverMessageDispatcher(conn, msg);
});
conn.on("close", function(){
delete SocketMgr[conn._sumeru_socket_id];
//FIXME 遍历SubscribeMgr 删掉对应的sub
});
});
/**
* 使用socket发送数据
* @param {Object} socketId
* @param {Object} type
* @param {Object} data
*/
messageSend = function(socketId, type, data){
var socket = SocketMgr[socketId];
if (!socket) {
return true;
};
var strdata = JSON.stringify({
type: type,
content: data
});
socket.write(strdata);
};
var collection = new MockCollection();
fw.publish = function(modelName, pubName, pubFunc){
//create a collection on server
//var collection = new mongodb.Collection(db, modelName);
pubFunc = pubFunc || function(callback){
/*return collection.find({}).toArray(function(err, items){
callback(items);
});*/
var rs = collection.find({});
return callback(rs);
};
PublishContainer[pubName] = {
modelName : modelName,
collection : collection,
handle : function(args, callback){
var _args = args;
_args.push(callback);
//如果subscribe时传递的参数和pubFunc本身接受的参数数量不一致,则忽略多余的参数,但要保证callback作为最后一位正确传递。
if(_args.length != pubFunc.length){
_args.splice(Math.max(0, pubFunc.length - 1));
_args.push(callback);
}
pubFunc.apply(collection, _args);
}
};
};
};
/**
* 接收到消息后的分发处理
* have access to db
* @param {Object} socket
* @param {Object} data
*/
var serverMessageDispatcher = function(socket, strrdata){
try {
data = JSON.parse(strrdata);
}
catch (e) {
//直接丢弃
console.error('error@msgDispatcher ' + data);
return;
}
if (!data.type) {
return;
}
var socketId = socket._sumeru_socket_id || 0;
console.log("retrieving " + strrdata);
if (data.type == 'echo') { //注册请求
var socketId = data.content.socketId;
SocketMgr[socketId] = socket;
socket._sumeru_socket_id = socketId;
//暂时现先在这里run起来server.js
require('../controller/real-time/server/server.js')(fw);
return;
}
/**
* type : subscript
* content : {
* name : pubname,
* args : [arg1, arg2, arg3]
* }
*
*/
if (data.type == 'subscribe') { //订阅请求
var pubname = data.content.name;
if (!PublishContainer[pubname]) {
return;
}
var args = data.content.args || [],
modelName = PublishContainer[pubname].modelName;
//FIXME 需要过滤modelName的值,使其符合object的key的要求
if (!SubscribeMgr[modelName]) {
SubscribeMgr[modelName] = [];
}
//这里传过来的args应该是从第二位开始的具体参数(第一位是pub的名字本身)
SubscribeMgr[modelName].push({
socketId: socketId,
pubname: pubname,
args: args
});
//fetch the publish record on server
var pubRecord = PublishContainer[pubname];
var collection = pubRecord.collection,
pubFunc = pubRecord.handle,
onComplete = function(dataArray){
//start to write_data to client
messageSend(socketId, 'data_write_from_server', {
pubname: pubname,
data: dataArray
});
};
//run publish function with args and callback
pubFunc.call(collection, args, onComplete);
return;
}
/**
* type : data_write
* content : {
* rs : [],
* where : [],
* pubname : pubname
* }
*/
if (data.type == 'data_write_from_client') { //写数据
var pubname = data.content.pubname;
if (!PublishContainer[pubname]) {
console.log('error finding a pub record', pubname);
return;
}
var pubRecord = PublishContainer[pubname];
var struct = data.content.data,
structData = struct.cnt;
if(struct.type == 'insert'){
var cid = delete structData.__clientId;
delete structData.__clientId;
delete structData._id;
pubRecord.collection.save(structData);
} else if (struct.type == 'delete'){
pubRecord.collection.remove({
_id : structData._id
});
} else if (struct.type == 'update'){
var _id = structData._id;
delete structData._id;
pubRecord.collection.update({
_id : _id
}, {
$set : structData
});
}
//写入数据
//FIXME 判断数组,逐行写入
/*if(!data.content.data._id){
delete data.content.data._id;
}*/
try {
}
catch (e) {
}
//find all subscriber
//FIXME 这里需要考虑model嵌套的问题
var subscribers = SubscribeMgr[pubRecord.modelName];
subscribers.forEach(function(item){
//do not skip current subscriber
//write back to make sure the consistence.
var pubname = item.pubname,
pubRecord = PublishContainer[pubname],
collection = pubRecord.collection,
pubFunc = pubRecord.handle;
pubFunc.call(collection, item.args, function(dataArray){
//推送回去
messageSend(item.socketId, 'data_write_from_server', {
pubname: pubname,
data: dataArray,
flag : 'live_data'
});
});
});
return;
}
};
runStub();
/*
var readModel = function(){
//读取所有的model定义,实例化,给所有model增加publish方法
};*/
/*
var model = function(){};
//考虑一下一个server对应所有app的情况怎么处理呢
//参数形式name, handle
//-- 对arguments有操作的方法不写形参,否则node中会覆盖原有arguments的值,chrome也一样
model.prototype.publish = function(){
var name = arguments[0],
handle = arguments[arguments.length - 1],
self = this,
defaultHandle = function(){return self.getData()};
if(handle == name){ //即没写handle
handle = defaultHandle;
}
PublishContainer[name] = {
modelName : self.name,
handle : function(args){
handle.apply(self, args);
}
};
};*/