2011-09-16

Remove SQL Server 2005 Express Tools - Install SQL Server Management Studio Express 2008, stupid installer

Khi install SQL Server Management Studio Express (SSME) bị cái lỗi "The SQL Server 2005 Express Tools are installed. To continue, remove the SQL Server 2005 Express Tools.".


Sau khi search Google mới tìm được cách pass qua đồ khi đó.
http://social.msdn.microsoft.com/Forums/en-US/sqlsetupandupgrade/thread/5fc58507-9f40-4213-acbd-32a57c8822d7/
http://dotnetguts.blogspot.com/2010/06/remove-sql-server-2005-express-tools.html

Solution
1) Open Run Menu and type regedit.
2) It will open Registry Editor
3) Now go to following location.
HKEY_LOCAL_MACHINE > Software > Microsoft > Microsoft SQL Server > 90

4) Remove Registry Key 90.

Thêm nữa cái installer của SSME installer cực kỳ ngu. Phải chọn install new instance, tất nhiên dù có chọn new instance thì cũng chằng có instance mới nào được install cả (yên tâm). Chỉ cần install bình thường với cách chọn new instance và check Management Tools - Basic. Thế là xong.

2011-09-12

Node.js evented I/O for V8 Javascript.

What is Node.js

Hôm nay mình sẽ note lại mình đã play với Node.js như thế nào. Thực ra thì mình cũng biết sơ về cái món này. Biết thông qua website/blog của Felix http://debuggable.com/. Developer này tất nhiên là quá quen đối với những người xài CakePHP và anh ta hiện giờ chuyển wa chơi với Node.js là core contributor. Mới đầu thì cũng chẳng biết nó là gì, sau thấy viết code server-side bằng Javascript trông rất sáng sủa, mình cũng mê code Javascript, có một chút gì đó tự do :D. Sau một thời gian làm và OK đã biết chút chút thì note lại cho khỏi quên.

Đến version này thì đã có bản build node.exe cho Windows, trước kia thì phải install Cygwin. OK nói đến đây thôi chỉ để biết nó là server-side Javascript. Và nên biết thêm V8 Javascript Engine là gì. Giờ thì play 1 chút.

Installation
Download node.exe dành cho Windows về, chạy lên chỉ ra màn hình có dấu nhắc lệnh > như command prompt. Với Linux hay Mac thì install cũng không mấy khó khăn. Nếu có thể install git hay download source tar và make là xong. Mình lấy Windows làm dev enviroment.


Giờ tạo một function đơn giản thực hiện phép cộng, nhấn Enter nó sẽ tự động indent bằng ... cho mình như trong hình. Cách viết như các ngôn ngữ script khác gọi là REPL read-eval-print loop. Mình không đi sâu nó là gì, chỉ cần biết dùng thử ntn đã.


Felix có một trang gọi là study guide rất đáng để tham khảo.

Demo
Vậy tiếp theo làm gì, tất nhiên không viết code trên cái màn hình như cmd đó được. OK copy cái ví dụ trên trang chủ http://www.nodejs.org/  thành một file gọi là server.js saved cùng thư mục với node.exe. Server này mở port 1337, trả về 200 OK result là Hello World với tất cả request, một simple dummy web server.
  
var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(1337, "127.0.0.1");
console.log('Server running at http://127.0.0.1:1337/');

Chúng ta sẽ chạy script này, để thuận tiện từ đây chúng ta dùng PowerShell dùng command line cũng OK. Với Windows 7 thì PowerShell được shift sẵn, bình thường mình găm nó vào taskbar luôn (taskbar của mình theo thứ tự là Windows Media Player, Unikey, PowerShell). Nếu chạy bình thường thì gọi Windows + R -> gõ powershell. Nếu đã găm vào taskbar thì đơn giản Windows + số thứ tự trên taskbar như mình là Windows + 3. OK launch PowerShell change dir (cd) tới file server.js và node.exe. Chạy command
  
.\node.exe .\server.js


Như vậy ta đã có server, port 1337. Giờ test server bằng Firefox chẳng hạn, localhost:1337 sẽ được hello string. Stop server đơn giản như terminate command, Ctrl + C. Tới đây dể thấy tạo server đơn giản như thế nào.

 

Các vấn đề khác thuộc về cách sử dụng hay syntax hay style; ví dụ như closure, sử dụng module, callback; thì có lẽ tự tìm hiểu sẽ tốt hơn là nói ở đây.

What next?
Nhưng giờ có con dao này rồi sẽ làm gì, viết web thì với những gì có thể làm được với PHP -> say no vì ko thể cầm con dao nhỏ mà giết chết con khủng long. Nếu tham khảo bài viết của Felix bạn sẽ biết một vài trường hợp mà Felix kêu là bad use cases.

Vậy nó làm gì, hiện tại thì chưa có gì nhiều nhưng nếu dùng build một light-weight service thì rất phù hợp. Ví dụ một REST API service hay chat server. Nếu để ý thì rất nhiều chat server đã triển khai dùng Node.js. Có thể kể là Ajax IM hay Faye và vô số những ví dụ trên GitHub chỉ cần search 1 chút là ra. Faye giới thiệu là implement Bayeux protocol. Mình không đi sâu mô tả protocol này nhưng mình không lựa chọn Faye cũng như chỉ tham khảo Ajax IM. Có lẽ bạn nên chọn Node.js + Socket.IO và thử viết cái gì đó, rồi từ từ cải tiến theo cách bạn muốn, có thể là tổ chức lại hay implement theo Bayeux.

Authentication with Node.js and Socket.IO
Vấn đề tiếp theo mình đề cập lại việc chứng thực giữa Node.js và Socket.IO. Socket.IO có mục config gọi là handshake function như sau:

// create io
var io = sio.listen(PORT);

io.configure(function() {
    io.set('authorization', function (data, callback) {
        authorizePhpSession(data, callback);
    });
});

Xem thêm tài liệu của Socket.IO tại đây https://github.com/LearnBoost/socket.io/wiki/Authorizing

Bạn có thể tham khảo từ bài viết sau http://anthonyw.net/2011/07/authentication-with-node-js-and-zend-framework/. Tuy nhiên mình sẽ nói rõ hơn chút xíu.

Client khi connect sẽ gửi thông tin lên server bao gồm cả cookie và các thông tin khác. Như vậy hình dung ta có scenario như sau: user login vào site theo cách thông thường, web server sẽ lưu lại thông tin của user trong session: có thể là file session hay memcache. Giả sử trong PHP dùng file session thì thông tin sẽ trong một file mà server sinh ra trong thư mục chỉ định dạng sess_{session-id}. Khi user đã login và chuyển qua page chat, Socket.IO client (Javascript) sẽ kết nối với chat server (Node.js server), Node.js phải thực hiện authorization, quyết định cho phép client connect hay không thông qua thông tin của client cung cấp. Ta chú ý đến thông tin Session ID sẽ được client send thông qua cookie. Nếu OK sẽ gọi callback với tham số true, ngược lại là false và error. Giả sử web server chạy PHP và không dùng memcache. Khi đó session file sẽ có dạng

var sessionFile = SESSION_PATH + "/sess_" + sessionId;

Thực hiện authorize bằng session như sau:

function authorizePhpSession(data, callback) {
    Controller.log("Get handshake data from client: " + sys.inspect(data));

    var sessionId = getPhpSessionId(data.headers.cookie);
    Controller.log("Receive session ID: " + sessionId);

    // extend handshake data with PHP Session ID
    data.phpSessionId = sessionId;

    try {
        if(!USE_MEMCACHE) {
            getFileSessionData(data, sessionId, callback);
        } else {
            getMemcacheSessionData(data, sessionId, callback);
        }
    } catch(e) {
        callback(null, false);
    }
}

Lấy PHP SESSION ID trong cookies gửi lên. Lưu ý domain của web site và Node.js server phải giống nhau thì client mới send thông tin cookies.

function getPhpSessionId(cookies) {
    var result = "";
    cookies.split(";").forEach(function(a) {
        var b = a.split("=");
        if(b[0].trim() == PHP_SESSION) {
            result = b[1].trim();
        }
    });

    return result;
}

Example đọc thông tin session trong file

function getFileSessionData(data, sessionId, callback) {
    Controller.log("Get session data from session file with Session ID: " + sessionId);

    var sessionFile = SESSION_PATH + "/sess_" + sessionId;      
    try {
        fs.readFile(sessionFile, "utf8", function(err, fileData) {
            if(err) {   // not exists session
                Controller.log("Can not read session data from file path { session: '" +
            sessionFile + "', error: '" + sys.inspect(err) + "' }");
                callback(null, false);
            } else {
        if(fileData.indexOf(SESSION_AUTH + "|") == -1) {    // not logged in yet
            // not things here, not authorize for this connection
            callback(null, false);
        } else {
            fileData = fileData.substr((SESSION_AUTH + "|").length);

    // extend handshake data with user session data
            var sobj = utils.unserialize(fileData);

    if(sobj.storage) {
                data.user = sobj.storage;
                callback(null, true);
            } else {
        Controller.log("Not valid authentication session data for { session: '" +
            sessionId + "', result: '" + fileData + "' }");
                callback(null, false);
            }
                }
            }
        });
    } catch(e) {
        Controller.log("Error when get authentication information { session: '" + sessionId +
            "', error: '" + sys.inspect(e) + "' }");
        callback(null, false);
    }
}

Có thể dùng RegEx để tìm thông tin authentication thay vì unserialize string trong trường hợp thông tin session quá dài và quá khó để unserialize.

function getMemcacheSessionData(data, sessionId, callback) {
    Controller.log("Get session data from Memcache Server with Session ID: " + sessionId);

    try {
        memcache.get(sessionId, function(err, result) {
            if(err) {
               Controller.log("Can not read session data from Memcache Server { session: '" +
                    sessionId + "', error: '" + sys.inspect(err) + "' }");
               callback(null, false);
          } else {
              var match = result.match(SESSION_REGEX); // using regex to extract authentication information
              if (match != null) {
                  data.user = {
                     id: match[1],
                     username: match[2],
                     email: match[3],
                     first_name: (match[4] == null || typeof match[4] == "undefined") ? "" : match[4],
                     last_name: (match[5] == null || typeof match[5] == "undefined") ? "" : match[5],
                  }

                  Controller.log("Authentication information { session: '" +
                     sessionId + "', user: '" + sys.inspect(data.user) + "' }");
                  callback(null, true);
              } else {
                  Controller.log("Not valid authentication session data for { session: '" +
                     sessionId + "', result: '" + result + "' }");
                  callback(null, false);
              }
          }
        });
    } catch(e) {
        Controller.log("Error when get authentication information { session: '" + sessionId +
                    "', error: '" + sys.inspect(e) + "' }");
        callback(null, false);
    }
}

Còn một phần nữa nói về cách giao tiếp giữa Node.js server và Java server khác như game server (vì thường game server implement bằng Java. Giải pháp của mình là dùng message queue như RabbitMQ, tuy nhiên có lẽ đề một dịp khác sẽ giới thiệu.