庄周梦蝶

生活、程序、未来
   :: 首页 ::  ::  :: 聚合  :: 管理

    这一节主要是介绍局部状态变量,介绍了set!和begin的语法,看来ruby使用!号来表示改变变量值不是什么新鲜主意。
  
习题3.1,不解释了
;习题3.1
(define (make-accumulator init)
  (define (accumulator num)
    (set! init (+ init num))
    init)
  accumulator)

习题3.2,非常有趣的例子,在内部维持一个计数的变量即可,如果传入的参数是特定的符号就返回计数或者清0,如果不是,原过程调用。

;习题3.2
(define (make-monitored proc)
   (let ((counter 0))
  (define (proc-monitor args)
         (cond ((eq? args 'how-many-calls?) counter)
            ((eq? args 'reset-count) (begin (set! counter 0) counter))
            (else
              (begin (set! counter (+ counter 1)) (proc args)))))
  proc-monitor))

请注意,我的实现只能针对有一个参数的过程,对于多个参数的过程我还不知道怎么做。

习题3.3,passwd的局部状态变量,在dispatch前比较下传入的密码是否与之一致

;习题3.3
(define (make-account balance passwd)
  (define (withdraw amount)
    (if (>= balance amount)
        (begin (set! balance (- balance amount)) balance)
        "余额不足"))
  (define (deposit amount)
    (set! balance (+ balance amount))
    balance)
  (define (dispatch pwd m)
    (if (eq? pwd passwd)
        (cond ((eq? m 'withdraw) withdraw)
              ((eq? m 'deposit) deposit)
            (else
               (error "Unknow request--MAKE-ACCOUNT" m)))
        (lambda(x) "Incorrect password")))
        
  dispatch)
不一致的时候,返回一个匿名过程,仅仅是输出消息Incorrect password

习题3.4,在内部维持一个局部变量counter,用于计数密码错误的次数,在dispatch前判断counter是否等于7,如果是7就调用过程call-the-cops。

;习题3.4
(define (make-account balance passwd)
  (let ((counter 0))
  (define (withdraw amount)
    (if (>= balance amount)
        (begin (set! balance (- balance amount)) balance)
        "余额不足"))
  (define (deposit amount)
    (set! balance (+ balance amount))
    balance)
  (define (call-the-cops amount)
    "您已经尝试输入密码7次了!不能再试!")
  (define (dispatch pwd m)
    (cond ((= 7 counter) call-the-cops)
          ((eq? pwd passwd)
           (cond ((eq? m 'withdraw) withdraw)
                 ((eq? m 'deposit) deposit)
                 (else
                   (error "Unknow request--MAKE-ACCOUNT" m))))
          (else
            (begin (set! counter (+ counter 1)) (lambda(x) "Incorrect password")))))
 dispatch))




posted @ 2007-07-24 17:12 dennis 阅读(268) | 评论 (0)编辑 收藏

    暂时搞不到《Programming Erlang》,最近就一直在看Erlang自带的例子和Reference Manual。基础语法方面有一些过去遗漏或者没有注意的,断断续续仅记于此。

1。Erlang的保留字有:

after and andalso band begin bnot bor bsl bsr bxor case catch cond div end fun if let not of or orelse query receive rem try when xor

基本都是些用于逻辑运算、位运算以及特殊表达式的符号

2.Erlang的类型,除了在前面入门一提到的类型外,还包括:
1)Binary,用于表示某段未知类型的内存区域
比如:
1> <<10,20>>.
<<10,20>>
2> <<"ABC">>.
 <<65,66,67>>

2)Reference,通过调用mk_ref/0产生的运行时的unique term

3)String,字符串,Erlang中的字符串用双引号包括起来,其实也是list。编译时期,两个邻近的字符串将被连接起来,比如"string" "42" 等价于 "string42"

4)Record,记录类型,与c语言中的struct类似,模块可以通过-record属性声明,比如:
-module(person).
-export([new/2]).
-record(person, {name, age}).
new(Name, Age) ->
     #person{name=Name, age=Age}.
1> person:new(dennis, 44).
{person,dennis,44}
 在编译后其实已经被转化为tuple。可以通过Name#person.name来访问Name Record的name属性。

3.模块的预定义属性
-module(Module).    声明模块名称,必须与文件名相同
-export(Functions).   指定向外界导出的函数列表
-import(Module,Functions).   引入函数,引入的函数可以被当作本地定义的函数使用
-compile(Options).     设置编译选项,比如export_all
-vsn(Vsn).         模块版本,设置了此项,可以通过beam_lib:version/1 获取此项信息
可以通过-include和-include_lib来包含文件,两者的区别是include-lib不能通过绝对路径查找文件,而是在你当前Erlang的lib目录进行查找。

4.try表达式,try表达式可以与catch结合使用,比如:
try Expr
catch
throw:Term -> Term;
exit:Reason -> {'EXIT',Reason}
error:Reason -> {'EXIT',{Reason,erlang:get_stacktrace()}}
end

不仅如此,try还可以与after结合使用,类似java中的try..finally,用于进行清除作用,比如:
termize_file(Name) -> {ok,F} = file:open(Name, [read,binary]), try {ok,Bin} = file:read(F, 1024*1024), binary_to_term(Bin) after file:close(F) end.

5.列表推断(List Comprehensions),函数式语言特性之一,Erlang中的语法类似:
[Expr || Qualifier1,...,QualifierN] Expr可以是任意的表达式,而Qualifier是generator或者filter。还是各举例子说明下。
1> [X*2 || X <- [1,2,3]]. [2,4,6]

2> L=[1,2,3,4,5,6,7].
[1,2,3,4,5,6,7]
3> [X|X<-L,X>=3].
[
3,4,5,6,7]

再看几个比较酷的例子,来自Programming Erlang
比如快速排序
-module(qsort).
-export([qsort/1]).
qsort([])->[];
qsort([Pivot|T])->
  qsort([X||X<-T,X<Pivot])
   ++ [Pivot] ++
  qsort([X||X<-T,X>=Pivot]).

搜索勾股数组
%勾股数组
-module(pythag).
-export([pythag/1]).
pythag(N)->
    L=lists:seq(1,N),
    Square=fun(X) when is_number(X)->X*X end,
    [{A,B,C}||
        A<-L,
        B<-L,
        C<-L,
        A+B+C=<N,
        Square(A)+Square(B)=:=Square(C)].
列表推断将大大减少你的敲击键盘的次数。
 
6.宏,定义常量或者函数等等,语法如下:
-define(Const, Replacement). -define(Func(Var1,...,VarN), Replacement).
使用的时候在宏名前加个问号?,比如?Const,Replacement将插入宏出现的位置。系统预定义了一些宏:
?MODULE 表示当前模块名

?MODULE_STRING 同上,但是以字符串形式
?FILE 当前模块的文件名
?LINE 调用的当前代码行数
?MACHINE 机器名

Erlang的宏与C语言的宏很相似,同样有宏指示符,包括:
-undef(Macro).
取消宏定义
-ifdef(Macro).
当宏Macro有定义的时候,执行以下代码
-ifndef(Macro).
同上,反之
-else.
接在ifdef或者ifndef之后,表示不满足前者条件时执行以下代码
-endif.
if终止符
假设宏-define(Square(X),X*X).用于计算平方,那么??X将返回X表达式的字符串形式,类似C语言中#arg

一个简单的宏例子:
-module(macros_demo).
-ifdef(debug).
-define(LOG(X), io:format("{~p,~p}: ~p~n", [?MODULE,?LINE,X])).
-else.
-define(LOG(X), true).
-endif.
-define(Square(X),X*X).
-compile(export_all).
test()
->
    A
=3,
    ?LOG(A),
    B
=?Square(A),
    io:format(
"square(~w) is ~w~n",[A,B]).
当编译时不开启debug选项的时候:
17> c(macros_demo).
{ok,macros_demo}
18> macros_demo:test().
square(3) is 9

当编译时开启debug之后:

19> c(macros_demo,{d,debug}).
{ok,macros_demo}
20> macros_demo:test().
{macros_demo,11}: 3
square(3) is 9
ok

可以看到LOG的输出了,行数、模块名以及参数
7、Process Dictionary,每个进程都有自己的process dictionary,用于存储这个进程内的全局变量,可以通过下列
BIFs操作:
put(Key, Value)
get(Key)
get()
get_keys(Value)
erase(Key)
erase()

8、关于分布式编程,需要补充的几点
1)节点之间的连接默认是transitive,也就是当节点A连接了节点B,节点B连接了节点C,那么节点A也与节点C互相连接
可以通过启动节点时指定参数-connect_all false来取消默认行为

2)隐藏节点,某些情况下,你希望连接一个节点而不去连接其他节点,你可以通过在节点启动时指定-hidden选项
来启动一个hidden node。在此情况下,通过nodes()查看所有连接的节点将不会出现隐藏的节点,想看到隐藏的节点
可以通过nodes(hidden)或者nodes(connected)来查看。

完整的erl选项如下:
-connect_all false 上面已经解释。
-hidden 启动一个hidden node
-name Name 启动一个系统成为节点,使用long name.
-setcookie Cookie Erlang:set_cookie(node(), Cookie).相同,设置magic cookie
-sname Name 启动一个Erlang系统作为节点,使用short name

注意,short name启动的节点是无法与long name节点通信的

9.一个小细节,在Erlang中小于等于是用=<表示,而不是一般语言中的<=语法,我犯过错误的地方,同样,不等于都是用/号,而不是
!,比如/=、=/=。

10.and or 和andalso orelse的区别

and和or会计算两边的表达式,而andalso和orelse的求值采用短路机制,比如exp1 andalso exp2,当exp1返回false之后,就不会去求值
exp2,而是直接返回false,而exp1 and exp2会对exp1和exp2都进行求值,or与orelse也类似。
 
今天在erlang-china下到了《Programming Erlang》,准备打印一份看看,进入OTP的学习。  


 
 





posted @ 2007-07-24 11:23 dennis 阅读(8359) | 评论 (0)编辑 收藏

    来自sicp的完整代码,包括书中给出的代码以及习题,实现了huffman树的生成、解码、编码过程,总共67行代码,同样的代码有空用java、ruby改写下,看看会有什么不同。
(define (make-leaf symbol weight)
  (list 
'leaf symbol weight))
(define (leaf? object)
  (eq? (car object) 
'leaf))
(define (symbol-leaf x) (cadr x))
(define (weight
-leaf x) (caddr x))
;合并最低权重的两个节点
(define (make
-code-tree left right)
  (list left right (append (symbols left) (symbols right)) (
+ (weight left) (weight right))))
(define (left
-branch tree) (car tree))
(define (right
-branch tree) (cadr tree))
(define (symbols tree)
  (
if (leaf? tree)
      (list (symbol
-leaf tree))
      (caddr tree)))
(define (weight tree)
  (
if (leaf? tree)
      (weight
-leaf tree)
      (cadddr tree)))
;解码
(define (decode bits tree)
  (define (decode
-1 bits current-branch)
    (
if (null? bits)
        
'()
        (let ((next-branch
              (choose
-branch (car bits) current-branch)))
          (
if (leaf? next-branch)
              (cons (symbol
-leaf next-branch)
                    (decode
-1 (cdr bits) tree))
              (decode
-1 (cdr bits) next-branch)))))
  (decode
-1 bits tree))
(define (choose
-branch bit branch)
  (cond ((
= bit 0) (left-branch branch))
        ((
= bit 1) (right-branch branch))
        (
else (display "bad bit --CHOOSE-BRANCH"))))
(define (adjoin
-set x set)
  (cond ((null? set) (list x))
        ((
< (weight x) (weight (car set))) (cons x set))
        (
else
           (cons (car set) (adjoin
-set x (cdr set))))))
(define (make
-leaf-set pairs)
  (
if (null? pairs)
      
'()
      (let ((pair (car pairs)))
        (adjoin
-set (make-leaf (car pair) (cadr pair)) (make-leaf-set (cdr pairs))))))

;编码
(define (encode message tree)
  (
if (null? message)
      
'()
      (append (encode-symbol (car message) tree)
              (encode (cdr message) tree))))
(define (encode
-symbol symbol tree)
  (define (iter branch)
    (
if (leaf? branch)
        
'()
        (if (memq symbol (symbols (left-branch branch)))
            (cons 0 (iter (left
-branch branch)))
            (cons 
1 (iter (right-branch branch))))
        ))
  (
if (memq symbol (symbols tree))
      (iter tree)
      (display 
"bad symbol -- UNKNOWN SYMBOL")))
;生成hufman树
(define (generate
-huffman-tree pairs)
  (successive
-merge (make-leaf-set pairs)))

(define (successive
-merge leaf-set)
  (
if (null? (cdr leaf-set))
      (car leaf
-set)
      (successive
-merge (adjoin-set (make-code-tree (car leaf-set)
                                                    (cadr leaf
-set))
                                    (cddr leaf
-set)))))



posted @ 2007-07-23 08:56 dennis 阅读(964) | 评论 (0)编辑 收藏


    这一节那是相当的有趣,抽象数据的多重表示:采用标志(tag)来区分和数据导向(data-directed)技术,稍微提了下消息传递。通过一张二维表格将类型、操作的分派机制介绍的很清楚,静态OO语言正是通过类型来决定消息的分派,而消息传递以列进行划分,每个类型都以过程来表征,也就是所谓的“智能数据对象”,两者各有优缺点。当类型增加频繁时,消息传递风格的分派更容易扩展,当操作增加频繁时,反而是显式的类型分派更为容易扩展,这一点如果有OO设计经验应该很容易体会。
看看习题:

习题2.73,将求导程序以数据导向方式进行修改:
a)题目已经说了,基于被求导表达式的类型进行分派,加法表达式或者乘法表达式都有标志来区分,而number?和variable?并没有标志进行区分,因此无法加入数据导向分派。
b)选择函数不变,将求导过程提取出来,根据表达式类型划分,然后用put过程安装:
(define (deriv-sum exp var)
  (make
-sum (deriv (addend exp) var)
            (deriv (augend 
exp) var)))

(define (deriv
-prod exp var)
  (make
-sum
   (make
-product (multiplier exp)
                 (deriv (multiplicand 
exp) var))
   (make
-product (deriv (multiplier exp) var)
                 (multiplicand 
exp))))

(define (install
-deriv-package)
  (put 
'deriv '+ deriv-sum)
  (put 
'deriv '* deriv-prod)
  
'done)

c)我没做,不过和b差不了多少

习题2.75,很简单,跟着书上来:
(define (make-from-mag-ang r a)
  (define (dispatch op)
    (cond ((eq
? op 'real-part) (* r (cos a)))
          ((eq? op 
'imag-part) (* r (sin a)))
          ((eq
? op 'magnitude) r)
          ((eq? op 
'angle) a)
          (
else
             display 
"Unknow op")))
  dispatch)
将极角坐标系表示的复数用dispatch过程来表示,这正是消息传递风格。

习题2.76,在本文开头已经提了。

posted @ 2007-07-20 14:32 dennis 阅读(505) | 评论 (0)编辑 收藏

    先让我们来了解下柏拉图对世界的理解,柏拉图认为,自然界中有形的东西是流动的,但是构成这些有形物质的“形式”或“理念”却是永恒不变的。柏拉图指出,当我们说到“马”时,我们没有指任何一 匹马,而是称任何一种马。而“马”的含义本身独立于各种马(“有形的”),它不存在于空间和时间中,因此是永恒的。但是某一匹特定的、有形的、存在于感官 世界的马,却是“流动”的,会死亡,会腐烂。柏拉图把这个永恒不变的“形式”称为“理型”,他认为这个世界分为两个部分,一部分是完美的由“理型”组成的世界,另一个就是我们现实的“粗糙”的世界,真实世界是按照理型世界的标准设计的,总是努力达致完美的理型世界,但总是存有偏差。 因此他相信人类的灵魂是不朽的,灵魂苏醒后会向往回到完美“理型”的世界,人生来就有“理型”的观念。总之,柏拉图深信“理性”远比“感官”可靠。而他的学生亚里士多德却反驳他的老师,在亚里士多德看来,他也赞成世界是有变化的“质料”与不变的“形式”组成,比如具体的某只马是有一些“质料”组成,它有马的“形式”在里头,因此它被称为马。而“形式”存在于具体的“物”之中,比如“马”的含义就存在具体的千千万万只马之中, “形式”的观念并不是人与生俱来的,而是借助于“感官”去感知外界事物而得来的。他把这种“形式”更多地称为“特征”,由此提出了他的自然界分类观点,对“物”分类依据的是它能做什么以及它有什么特征。可以说,亚里士多德比他的老师更相信“感官”,没有像他的老师那样陷入对完美“理型世界”的眷念而不可自拔。
    联想到我们在使用静态OO语言(比如java)试图去描述现实世界的场景,我们总是试图先设计出一些类(class),这些类秉承我们的意志,我们预期它们能完美地描述事物,并且试图去符合所有的现实的场景。可以注意到,这样的想法不正是柏拉图式的偏执?我们用头脑中出现的“理型”(具体到语言中的类)去描述现实世界,可现实世界往往是模糊的、粗糙的,两者的冲突不可避免,导致类不再按照我们的设想发展,它变的庞大,变的不是那么清晰,因此我们又造出“设计模式”“重构”的“诡辩之术”去弥补、去完整,可这并不是治本之道。首先我们必须承认世界是不完美的,完美的“理型”或者说完美的类是不存在的,类型的划分不能依赖于头脑中的完美“理型”(类型的划分不是取决于类),而应该根据事物的特征以及事物能做什么来划分。放弃对类描述世界的追求,转而构造模糊的类型,对象的类型不再预先构造,它的类型取决于它能干什么,它有什么特征,这正是动态语言中的“Duck-Typing”以及一些函数式语言中的模式匹配希望做到的,两者都是为了描述模糊的现实世界。再比如ruby中的mixin和open class特性又提供了方式让我们去慢慢完善“粗糙”的类,放弃一蹴而就,选择有机成长。
    不是很清晰的想法,只是看《苏菲的世界》里对希腊古典哲学的描述突然想到的,各位权当荒唐言。


posted @ 2007-07-17 09:20 dennis 阅读(1509) | 评论 (8)编辑 收藏

    portal开发的一个重要工作就是portlet的开发,网上一大堆hello world,都是最简单的,用PrintWriter输出字符串。今天介绍一下稍微复杂的,用户转到portlet的edit Model,该页面是有一个输入框和一个按钮,输入你的名字,然后提交,将转到此portlet的view Model并输出hello,your name。
    portlet与servlet非常相似,因为portlet就是在servlet规范的基础上扩展的,并且portlet容器也是在servlet容器的基础上扩展,同serlvet一样,我们的HelloWorldPorlet需要强制继承自GenericPortlet类,GenericPortlet实现了Portlet接口,看看完整代码:
package net.rubyeye.portlets;
import javax.portlet.ActionResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

public class HelloWorldPortlet extends GenericPortlet {

        
@Override
        protected void doEdit(RenderRequest req
, RenderResponse res)
                        throws PortletException
, IOException {
                res
.setContentType("text/html");
                
//获取初始参数,在init-param中定义
                String jspName 
= getPortletConfig().getInitParameter("edit-jsp");
                PortletRequestDispatcher rd 
= getPortletContext().getRequestDispatcher(
                                jspName);
                rd
.include(req, res);
        }

        
@Override
        protected void doView(RenderRequest req
, RenderResponse res)
                        throws PortletException
, IOException {
                res
.setContentType("text/html");
                
//执行完processAction后设置了renderParamter,这里得到
                String msg 
= req.getParameter("msg");
               
                PrintWriter out 
= res.getWriter();
                
System.out.println(msg);
                
//有就输出
                
if (msg != null)
                        out
.write(msg);
                
//默认输出是Hello World
                
else
                        out
.write("Hello World!View!");

        }
        
//actionURL调用的processAction
        
@Override
        public void processAction(ActionRequest req
, ActionResponse res)
                        throws PortletException
, IOException {
                
// TODO Auto-generated method stub
                String name 
= req.getParameter("name");
                
System.out.println(name);
                res
.setRenderParameter("msg", "hello," + name);
        }
}
    解释下代码,portlet默认有三种Model:view、edit和help,顾名思义分别对照浏览、编辑和帮助状态,其中只有view是必须的,当然还可以自定义model。在portlet就有相应的doXXX方法用于呈现相应的片段,portlet还有一个render(RenderRequest,RenderResponse),这个方法与doView等方法的关系类似serlvet中service与doGet、doHelp的关系,一般我们都不直接覆写render,而是实现具体的doXXX方法。回到这个例子,HelloWorldPortlet有两个Model:view和edit。
    在doView方法中很简单,首先需要设置ContentType,然后获取前面processAction设置的renderParamter,当然,一开始进入portlet是view,这个值根本没有,默认就用PrintWriter输出Hello World。
    processAction用于处理ActionRequest的方法,和一般的servlet编程没有什么不同,请注意,processAction执行之后才是执行相应的render方法,并且与render不共享parameter,因此为了在doView中获得msg参数,我们需要通过ActionResponse的setRenderParameter来设定RenderRequest的Parameter。
    doEdit方法就比较简单了,通过PortletConfig的getInitParameter(与ServletConfig一样了)获得属性edit-jsp的值——编辑页面的位置,然后将该页面include进portlet。
    portlet需要一个配置文件来告诉portlets容器具体信息,这个文件同样放在WEB-INF目录下,并且名为portlet.xml:
<?xml version="1.0" encoding="UTF-8"?>
<portlet-app version="1.0"
        xmlns
="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
        xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance">

        
<portlet>
                
<portlet-name>HelloWorldPorlet_1</portlet-name>
                
<portlet-class>
                        net.rubyeye.porlets.HelloWorldPorlet
                
</portlet-class>
                
<init-param>
                        
<name>edit-jsp</name>
                        
<value>/portlets/edit.jsp</value>
                
</init-param>
                
<supports>
                        
<mime-type>text/html</mime-type>
                        
<portlet-mode>view</portlet-mode>
                        
<portlet-mode>edit</portlet-mode>
                
</supports>
                
<portlet-info>
                        
<title>HelloWorld</title>
                
</portlet-info>
        
</portlet>
</portlet-app>
  信息非常清楚,就不多做解释了,edit-jsp的值是/portlets/edit.jsp,portlets容器就会去portlets目录下寻找edit.jsp。supports元素指定了portlet支持的Model以及Mime类型。更详细的配置请参考官方规范
   最后就是edit.jsp,需要用到portlets规范规定的标签,我们这里用到了actionURL,该标签用于form的action的生成,触发带有参数的request请求的processAction调用,当然还有一个renderURL,顾名思义仅是用于呈现,而非action的触发。请注意,form的method必须是post,因为portlets容器可能将一些状态信息写在url,使用post提交避免冲突。
<%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
<portlet:defineObjects/>
<div>
<form method="post" action="<portlet:actionURL portletMode="view" windowState="normal"/>" >
please input your name:
<input type="text" name="name" size="10"/>
<input type="submit" value="submit"/>
</form>

  在使用portlet规范规定的标签前,一定要先使用<portlet:defineObjects/> 标签,以便定义下列变量让jsp使用:
renderRequest
renderResponse
portletConfig

    我们通过actionURL标签产生指向本portlet的url,并且指定没有错误发生时这个portlet应该具有的Model是view,窗口大小为normal。最后部署并运行,因为不同的portal的部署方式和所需要的其他配置文件各不相同,这里就不详细介绍了。下面是在Bea Portal 10.0上的效果图:

1.一开始看到的是view Model,输出Hello World!View!:


2.点击左上角的edit链接进入edit model:


3.输入dennis,提交,转到view model:


posted @ 2007-07-15 21:02 dennis 阅读(4828) | 评论 (1)编辑 收藏

    这两天将网上下载的《JSR168 porlet标准手册汉化整理》和《Liferay Portal二次开发指南》认认真真地看了几遍,看了BEA站点上portal的getting start,写了hello world,算是正式开始学习portal了。感谢这么多人无私地分享知识。抄书。

第一节 Portal 规范
随着Portal 的兴起,越来越多的公司开始涉足Portal 产品开发,并组建各自的Portal 组
件和基于其的产品,比如IBM、BEA、MicroSoft、SAP、Apache 等。各个厂商的接口互不
兼容,给软件开发商以及开发人员带来诸多不便。
1.1.1 JSR168
为此,JCP 组织发布了JSR168(Java Specification Request),Portlet Specification V1.0,
用来提供不同的Portal 和Portlet 之间的互通性。只要开发的Portlet 遵循JSR168,则就可以
在所有遵循JSR168 的Portal 上部署运行。
JSR168 中定义了Portal 的实现规范和接口,并对理想的Portlet 进行了详细的规划和描
述。
1.1.2 WSRP
WSRP 是OASIS Web Service for Remote Portlet 的缩写。WSRP 是Web Service 的一种新
的商业应用,一种新的标准,主要用来简化Portal 对于各种资源或者程序整合的复杂度,可
以避免编程带来的整合麻烦和问题。而且Portal 管理员可以从海量的WSRP 服务中选择需
要的功能用以整合到目前所用的Portal 中。它有三种角色:
①、生产者 􀃆 提供Portlet
②、消费者 􀃆 使用Portlet
③、终端用户 􀃆 最终用户
它的特点在于生产者将消费者所需要的信息通过WSRP 返回给消费者,这些信息是相对
标记片断,例如HTML、XHTML 等,可以直接嵌入用户的页面中,而不用像Web Service
一样开发用户端接口。
实现这个规范,Portal 可以跟各式各样的数据源打交道,彻底终结信息孤岛的窘境。
第二节 什么是Portal
Portal 是基于Web 的,以“应用整合”和“消除信息孤岛”为最终目的,提供单点登
录、内容聚合、个性化门户定制等功能的综合信息系统。
完整的Portal 通常由Portal 服务器、Portlet 容器、Portlet 构成。
1.2.1 Portal 服务器
Portal 服务器是容纳Portlet 容器,支持Portlet 呈现的普通或者特殊Web 服务器。
Portal 服务器通常会提供个性化设置、单点登录、内容聚合、信息发布、权限管理等功能,
支持各种信息数据来源,并将这些数据信息放在网页中组合而成,提供个性化的内容定制,
不同权限的浏览者能够浏览不同的信息内容。通常,Portal 提供以下功能:
单点登录:Portal 通常采用ACL、SSL、LDAP 等业界标准的安全技术,提供对所有现有
应用系统的安全集成,只需在Portal 的唯一入口上登录一次,就可以访问所有应用系统和
数据。对于安全性要求较高的应用系统,如电子商务平台、交易系统等,通过扩展接口传递
用户身份信息,如数字证书信息、数字签名信息等,进行二次身份认证,保证单点登陆的安
全性。
权限控制:系统采用LDAP 对用户资源进行统一的管理,同时提供二次开发接口,可以
与其他应用系统的用户管理模块对接,并能随相关业务系统实时更新访问权限。通过完善的
授权机制及存取控制,用户访问权限控制到字段级别,确保用户只能访问具有权限的应用系
统及相关信息。
内容管理: 实现应用系统之间实时交换信息。采用多种缓存机制,保证内容交换的性能
和准确性。采用基于XML 的Rich Site Summary (RSS)标准,迅速在各应用系统之间传播最
新变化。
信息发布: 实现信息门户内容的动态维护。动态网站系统可与OA 协同办公系统、知识
管理系统等集成,网站信息须经OA 系统的审批流程流转通过后或知识管理平台设置具有外
部共享权限后才可正式发布,真正实现内外信息发布的同步。
文件管理: 系统实现无缝集成多种数据源,包括:数据库、文档(Office 文档、PDF、
AutoCAD、甚至ZIP 文档)、Web 网页、FTP 站点等,并对数据按业务要求和职务特点加以分
析整理,通过统一Web 界面主动推送(Push)至用户的门户桌面,帮助用户做出及时、正确的
决策。
1.2.2 Portlet 容器
Portlet 容器提供Portlet 执行的环境,包含很多Portlet 并管理它们的生命周期,保
存Portlet 的定制信息。
一个Portal 容器接收到来自Portal 的请求后,接着将这个请求传递给存在Portal 容
器的Portlet 执行。Portlet 容器没有义务去组合Portlet 产生的信息內容,这个工作必
须由Portal 来处理。Portal 和 Portal 容器可以放在一起视为同一个系统的组件,或者分
开成为两个独立的组件。
Portlet 容器是普通Web Servlet 容器的扩展,所以一个Portlet 容器可以构建于一个
已经存在的Servlet 容器或者可能实现全部Web Servlet 容器的全部功能。无论Portlet
容器怎么实现,它的运行环境总是假定它支持Servlet2.3 规范。

通常,Portlet 容器扩展自普通的Servlet 容器。
第三节 什么是Portlet
Portlet 是Portal 中最重要的组件,负责在Portal 中呈现信息内容,有相应的生命周
期。通过自定义Portlet,用户很容易定义个性化的Portal 页面。Portlet 由Portlet 容器
负责管理、处理请求并返回动态页面,可以作为Portal 的可即插即用的界面组件。
1.3.1 Portlet
一个Portlet是以Java技术为技术的Web组件,由Portlet容器所管理,专门处理客户的
信息请求以及产生各种动态的信息内容。Portlet 为可插式的客户界面组件,提供呈现层成
为一个信息系统。
这些由Portlet产生的内容也被称为片段,而片段是具有一些规则的标记( HTML、XHTML、
WML ),而且可以和其他的片段组合而成一个复杂的文件。一个或多个 Portlet 的内容聚合
而成为一个 Portal 网页。而 Portlet 的生命周期是被 Portlet 容器所管理控制的。
客户端和Portlet的互动是由Portal通过典型的请求/响应方式实现,正常来说,客户会
和Portlet所产生的内容互动,举例来说,根据下一步的连接或者是确认送出的表单,结果
Portal将会接收到Portlet的动作,将这个处理状况转向到目标Portlet。这些Portlet 内容
的产生可能会因为不同的使用者而有不同的变化,完全是根据客户对于这个Portlet的设置。
1.3.2 Portlet 与Servlet 的关系
Portlet 被定义成为一个新的组件,具有新的明确的界面与行为。为了尽可能与现有的
Servlet 结合达到重复使用的目的,Portlet 的规范利用了 Servlet 的规范,许多观念都
很相似的,结合 Portlet、Servlet 及 Jsp 在同一个网站系统中,我们称为Portlet 应用 。
在同一个 Portlet 应用 中,他们将分享同一个类加载器(ClassLoader),上下文(Context)
及 Session。
①、Portlet 和 Servlet 的相似之处
@ Portlet 也是 Java 技术的 web 组件
@ Portlet 也是有特定的 container 在管理
@ Portlet 可以动态产生各种内容
@ Portlet 的生命周期由 container 所管理
@ Portlet 和客户端的互动是通过 request/response 的机制
②、Portlet 和 Servlet 也有一些不同
8
@ Portlet 只产生 markup 信息片段,不是完整的网页文件。而 Portal 会将所有的
Portlet markup 信息片段放到一个完整的 Portal 网页。
@ Portlet 不会和 URL 有直接的关系
@ 客户端必须通过 portal 系统才能和 Portlet 互动
@ Portlet 有一些定义好的 request 处理,action request 以及 render request。
@ Portlet 默认定义 Portlet modes 及窗口状态可以指出在网页中该 Portlet 的哪个功
能正在执行及现在的 状态。
@ Portlet 可以在同一个 portal 网页之中存在多个。
③、Portlet 有一些附加的功能是 Servlet 所没有的
@ Portlet 能够存取及储存永久配置文件及定制资料。
@ Portlet 可以存取使用者数据
@ Portlet 具有 URL 的重写功能在文件中去动态建立连结,允许 portal server 不用去
知道如何在网页的片 段之中建立连结及动作。
@ Portlet 可以储存临时性的数据在 Portlet session 之中,拥有两个不同的范围 :
application-wide scope 及 Portlet private scope 。
④、Portlet 不具有一些功能, 但是 Servlet 却有提供
@ Servlet 具有设置输出的文字编码( character set encoding)方式
@ Servlet 可以设置 HTTP 输出的 header
@ Servlet 才能够接收客户对于 portal 发出的 URL 请求
1.3.3 Portlet 的生命周期
一个Portlet有着良好的生命周期管理,定义了怎样装载,实例化和初始化,怎样响应来
自客户端的请求及怎样送出服务。这个Portlet生命周期由Portlet接口的init,processAction,
render和destroy方法来表达。
载入和实例化:Portlet 容器负责载入和实例化Portlet。当Portlet 容器运行Portlet 应用或
者延迟到Portlet 需要服务使用者的请求时,Portlet 就会被载入并实例化。载入Portlet 类后,
Portlet 类随即被实例化。
初始化:Portlet 类实例化后,Portlet 容器还需要初始化Portlet。以调用Portlet 去响应客
户端的请求。Portlet 容器呼叫Portlet 接口中的init 方法初始化Portlet。扩展自PortletConfig
的类可以取出定义在部署描述文件中的初始化参数,以及Resource Bundle。
初始化异常:在 Portlet 初始化期间,Portlet 可能会丟出 UnavailableException 或
PortletException 异常。此时,Portlet 容器不能把 Portlet 置入已启动的服务,并且 Portlet
容器必需释放这个 Portlet。 destory 方法不能被呼叫,因为初始化被认为执行失败。发生 失
败后,Portlet 容器会尝试着重新实例化及初始化 Portlet。这个异常处理的规则是:由一个
UnavailableException 指定一个不能执行的最小时间,当此异常发生时,Portlet 容器必需等
到指定时间过去后才产生并且初始化一个新的 Portlet。
在初始化过程中所丟出的 Runtime Exception 异常,被当作 PortletException 来处理。

posted @ 2007-07-15 20:16 dennis 阅读(1355) | 评论 (2)编辑 收藏

    Lich Ray写了个帖子《函数式编程语言曲高和寡?》,用快速排序的例子来说明函数式编程在表达思想方面比命令式语言更容易,其实这一点毋庸置疑,如果你正在读或者读过SICP的话。文中给了haskell、scheme和javascript的实现例子,我也凑趣写了个Erlang版本,haskell我不了解就不说了,其他实现分别如下:
scheme:
(define (qsort ls)  
     (
if (null? ls) '()  
         (let  
             ((x (car ls))  
             (xs (cdr ls)))  
             (let   
                 ((lt (filter (lambda (y) (< y x)) xs))  
                 (st (filter (lambda (y) (>= y x)) xs)))  
                 (append (qsort lt) (list x) (qsort st))))))  

javascript:
    // 把要用到的表达式抽象出来  
    Array
.prototype.head = function () {  
        
return this[0];  
    }  
      
    Array
.prototype.tail = function () {  
        
return this.slice(1);  
    }  
      
   Array
.prototype.filter = function (proc) {  
       var tmpArr 
= [];  
       
for (var i = 0; i < this.length; i++)  
       
if (proc(this[i]) == true)  
           tmpArr
.push(this[i]);  
       
return tmpArr;  
   }  
   Array
.prototype.qsort = function () {  
       
if (this == false) return []  
        var x
, xs, lt, st  
       x 
= this.head()  
        xs 
= this.tail()  
        lt 
= xs.filter(function (y) {return y < x})  
        st 
= xs.filter(function (y) {return y >= x})  
        
return lt.qsort().concat([x], st.qsort())  
    }  
用Erlang的话,Erlang的list其实跟scheme的list是一样的,甚至连定义的基本高阶函数都一样:map,filter,append等等,利用lists模块提供的filter和append,我们可以写出:
    qsort([])->[];  
    qsort([H
|T])->  
        Lt
=lists:filter(fun(E)->E<H end,T),  
        St
=lists:filter(fun(E)->E>=H end,T),  
    lists
:append(qsort(Lt),lists:append([H],qsort(St))).  
    我们来比较下scheme和Erlang版本,两者最显著的不同是,scheme使用了条件语句if,而Erlang却是通过模式匹配来代替条件分支判断。同样,在list的分解上面,Erlang也是利用了规则匹配来代替car,cdr函数,从这里可以看出规则匹配在Erlang中的主要作用:分解复杂数据结构以便赋值和条件分支的分派。
    扯远些可以谈到模式匹配是以“like-a”来代替消息分派在传统命令式语言中严格的“is-a”,这也跟现实世界的情况更为符合,现实世界中我们对事物的判断都是模糊。而这一点,不正是“Duck-Typing”?传统语言对于对象的类型(type)判断来源于严格确定对象是什么类(class),不是这个类它就没有相应的方法,而事实上类与类型这两个概念并不是一致的,对象的类型更应该根据对象能够做什么来决定。扯远了,这只是我读《失踪的链环》得来的感受,如果对模式匹配还有怀疑的话,让我们回到这个例子的Erlang版本,代码中我们调用了两次filter进行全表扫描,以便得到根据H切割的大小两个部分,这在性能上有不小的影响,那么我们能不能只进行一次全表扫描呢,返回结果是“大小”两个部分,看看Erlang应该怎么写:
sort([]) -> [];
sort([Pivot|Rest]) ->
   {Smaller, Bigger} = split(Pivot, Rest),
   lists:append(sort(Smaller), [Pivot|sort(Bigger)]).
split
(Pivot, L) ->
split(Pivot, L, [], []).
split(Pivot, [], Smaller, Bigger) ->
{Smaller
,Bigger};
split(Pivot, [H|T], Smaller, Bigger) when H < Pivot ->
split(Pivot, T, [H|Smaller], Bigger);
split(Pivot, [H|T], Smaller, Bigger) when H >= Pivot ->
split(Pivot, T, Smaller, [H|Bigger]).

    这几行代码充分展现了模式匹配的威力,不过Erlang其实有内置的方法partition用于切割list的,这里只是为了展现模式匹配,因此上面的代码可以改为:
sort([]) -> [];
sort([Pivot|Rest]) ->
{Smaller
, Bigger} = lists:partition(fun(E)->E<Pivot end, Rest),
lists
:append(sort(Smaller), [Pivot|sort(Bigger)]).

同样的代码改写为ruby版本:
def qsort(array)
  arr=array.dup
  
if arr==[]
    []
  
else
    x
=arr.shift
    smaller
,bigger=arr.partition{|e| e<=x}
    qsort(smaller)
+[x]+qsort(bigger)
  end
end
    ruby与Erlang都有并行赋值,但是ruby不支持模式匹配。请注意ruby并没有尾递归优化,因此上面的代码在数组比较大的时候会导致栈溢出,想用ruby做函数式编程应该尽量多使用循环和map,filter,collect等辅助高阶函数。
    另外一个Erlang与ruby、scheme比较重要的区别是Erlang的变量只能赋值一次(或者说绑定),也就是single assignment。这个特点与Erlang所要满足的运行场景有紧密关系,当系统发生错误时,就可以从原来的值重新启动任务,而不用担心由于变量值的变化导致系统恢复困难。




posted @ 2007-07-15 16:11 dennis 阅读(2997) | 评论 (0)编辑 收藏

如何设置一个基本的OpenLDAP Server
本文出自:http://www.linuxforum.net 作者:吴阿亭 Jephe wu (2001-09-04 15:00:01)

      一. 目的 

      本文旨在介绍如何安装OpenLDAP并且设置一个公司内部的集中化的邮件地址薄服务器供客
      户端查询。 
      基本上,OpenLDAPg还应用在其它许多方面,象集中化的用户帐号验证服务器,但邮件地址
      薄查询是最常用的。 

      二. 安装 

      从www.openldap.org下载最新的openldap软件包,按照编译和安装的步骤,依次运行:


      #tar cvfz openldap-stable-20010524.tgz 
      #cd openldap-2.0.11 
      #./configure 
      #make depend 
      #make 
      #make test 
      #make install 

      我的操作环境是redhat 6.1,如果没有遇到任何错误,最后默认安装LDAP后台程序slapd
      到目录/usr/local/libexec;配置文件在目录/usr/local/etc/openldap/ 并且放各种
      OpenLDAP工具
      ldapadd,ldapdelete,ldapmodify,ldapmodrdn,ldappasswd,ldapsearch 在目录
      /usr/local/bin,运行时数据库在/usr/local/var/openldap-ldbm 。 


      三. 设置 

      1) 更改配置文件/usr/local/etc/openldap/slapd.conf 
      在include /usr/local/etc/openldap/schema/core.schema这行后面加上下面的行,
      包括所有的方案。 

      include /usr/local/etc/openldap/schema/corba.schema 
      include /usr/local/etc/openldap/schema/cosine.schema 
      include /usr/local/etc/openldap/schema/inetorgperson.schema 
      include /usr/local/etc/openldap/schema/java.schema 
      include /usr/local/etc/openldap/schema/krb5-kdc.schema 
      include /usr/local/etc/openldap/schema/misc.schema 
      include /usr/local/etc/openldap/schema/nadf.schema 
      include /usr/local/etc/openldap/schema/nis.schema 
      include /usr/local/etc/openldap/schema/openldap.schema 

      2) 在文件slapd.conf的"ldbm database definitions"部分更改相应的
      suffix,rootdn行如下 

      database ldbm 
      suffix "o=yourdomain,c=us" 
      rootdn "cn=root,o=yourdomain,c=us" 
      rootpw secret 
      directory /usr/local/var/openldap-ldbm 

      有各种格式你可以用,这里我用的是o=yourdomain,c=us 说明你的公司域名和所在的国
      家或地区 
      rootdn的格式安装后默认为cn=Manager,这里改为root完全是自己的喜好,这样符合
      Unix/Linux中root具有最高权限的传统。 

      3) 现在可以启动slapd了,运行/usr/local/libexec/slapd 。 

      可以考虑把/usr/local/bin and /usr/local/libexec加到搜索路径中,即加到
      /etc/profile 
      中的PATH行: 
      PATH="$PATH:/usr/X11R6/bin:/usr/local/bin:/usr/local/libexec" 
      这样下次登录后只需键入 slapd 。 

      4) 测试ldap server是否正常工作。 
      运行下面的命令检查是否有相应的输出。 

      #ldapsearch -x -b 'o=yourdomain,c=us' '(objectclass=*)' 


      5) 编辑.ldif文本文件,用ldapadd添加记录进入LDAP数据库。 
      文件内容如下: 

      dn: o=yourdomain,c=us 
      objectclass: dcobject 
      objectclass: organization 
      o: yourdomain 
      dc: yourdomain 

      dn: cn=Jephe Wu,o=yourdomain,c=us 
      objectclass: inetorgperson 
      cn: Jephe Wu 
      sn: Wu 
      mail: jephe_wu@yourdomain.com 


      ......more users...... 

      依次类推,添加每个人的记录进入该文件中,注意对象类型 inetorgperson 至少必须要
      有cn和sn 
      ,这里我们用cn,sn,mail三项定义,这对我们的邮件地址薄功能来说已经足够。你还可以
      定义象 
      mobile, homephone,pager......等等。 

      然后用下面的命令添加上面的.ldif文件进入LDAP数据库 

      #ldapadd -x -D "cn=root,o=yourdomain,c=us" -w secret -f
      "yourldiffilename" 

      注:上面的文件的第一部分"dn: o=yourdomain,c=us"是必须的,否则不能添加数据。 
      用你的公司的域名替换上面的"yourdomain"。 

      6) 设置Outlook Express, 允许用LDAP服务器查询邮件地址。 

      "工具/帐号/添加--目录服务",填入你的服务器的IP地址或者主机全称域名,在下一个屏
      幕中选yes以允许用目录服务来查询地址,最后在"目录服务"栏中选中刚才设置的项目击
      “属性/高级",在"搜索库"中填入 
      "o=yourdomain,c=us" 。 
      Netscape请根据上面的信息设置相应的选项。 

      四. 常见使用问题 

      1) 能启动slapd 没有问题,但不能添加数据库,运行ldapadd添加时出错 "ldap_bind:
      cannot contact LDAP Server" 。 
      答: 最可能的原因是在/etc/hosts中没有127.0.0.1 localhost项目。 

      2) 注意查询顺序: 如果在Outlook Express的地址薄中有内容,则检查地址时地址薄优
      先,如果在本地地址薄中找不到相应记录,然后再查询LDAP服务器。 

      3) 用下面的命令确信客户端与LDAP服务器有通讯,在服务器运行下面的命令,然后在OE中
      测试检查地址,你将会得到查询LDAP数据库的连接过程的输出。 

      # tcpdump port 389 

posted @ 2007-07-14 16:08 dennis 阅读(1606) | 评论 (1)编辑 收藏

    在bea的官网下了10.0的weblogic portal,在redhat上安装了下,想跑个最简单的portal例子,部署一次几乎要20分钟,受不了,可是2G,P4的机器啊。KDE慢吞吞的响应速度本来就够郁闷了,加上这个可怕的部署时间,阿门。。。手头的portal资料先看看,项目的portal准备让我来接手,得预先准备了。在erlang-china看到说《Programming Erlang》准备找人翻译并出版了,是个好消息,不过看情况要看到中文版要等明年,我等不及了。

posted @ 2007-07-13 18:40 dennis 阅读(244) | 评论 (0)编辑 收藏

仅列出标题
共56页: First 上一页 36 37 38 39 40 41 42 43 44 下一页 Last