jme(j2me)的一些工具

从ee,se转到me会感觉被束缚了手脚,很多东西都需要自己写。其实不是,也有很多好用的framework可以拿来用

xml, kxml,xparser,前者是比较出名的xml处理,而后者是我在sf上偶然发现的,api比较清爽,但是需要稍微修改一下才可以用,因为他不仅仅是给me用的,所以要把source里的一些高级api(zipinputstream,bufferedinputstream)去掉。

logging, j4me,是googlecode上找到的,提供类似log4j的支持

unit test, j2meunit,没什么说的

IOC container, 没有,试图去找,结果是:IOC实现大多数严重依赖reflection,也就意味着必须要java1.3以上。而me只有1.1的级别

UI, j4me,提供一些常用的如menu和dialog的支持,没有用过

最后更新: 12-18-2007

Powered by ScribeFire.

简单的div显示,隐藏例子

<script type=”text/javascript” language=”JavaScript”><!–
function HideContent(d) {
if(d.length < 1) { return; }
document.getElementById(d).style.display = “none”;
}
function ShowContent(d) {
if(d.length < 1) { return; }
document.getElementById(d).style.display = “block”;
}
function ReverseContentDisplay0(d) {
if(d.length < 1) { return; }
if(document.getElementById(d).style.display == “none”) { document.getElementById(d).style.display = “block”; }
else { document.getElementById(d).style.display = “none”; }
}

function ReverseContentDisplay(d) {
if(d.length < 1) { return; }
for(i=1;i<=3;i++){
var tmp = ‘block’+i;
if(d==tmp){
if(document.getElementById(d).style.display == “none”) { document.getElementById(d).style.display = “block”; }
else { document.getElementById(d).style.display = “none”; }
}else{
document.getElementById(tmp).style.display = “none”;
}
}
}
//–></script>

<a
onclick=”javascript:ReverseContentDisplay(‘block1’); return true;”
href=”#”>
[Bid 1]
</a>
<div
id=”block1″
style=”display:none;
border-style: solid;
background-color: white;
padding: 5px;”>

bLOCK 1
</div>
<br>
<a
onclick=”javascript:ReverseContentDisplay(‘block2’); return true;”
href=”#”>
[Bid 2]
</a>
<div
id=”block2″
style=”display:none;
border-style: solid;
background-color: white;
padding: 5px;”>

bLOCK 2
</div>
<br>
<a
onclick=”javascript:ReverseContentDisplay(‘block3’); return true;”
href=”#”>
[Bid 3]
</a>
<div
id=”block3″
style=”display:none;
border-style: solid;
background-color: white;
padding: 5px;”>

bLOCK 3
</div>

Spring的FactoryBean和InitializingBean

刚刚“领略”了一些spring-modules的源代码看到了FactoryBean和InitiliazingBean的用处。记在这里

  • InitializingBean.afterPropertiesSet()

这个方法比较容易理解,是在spring根据配置文件设置了bean的属性后调用的,一般这里会做一些初始化的工作。比如,xml配置中只是提供了resource的路径,在这个方法里 就要根据resource的路径初始化一些成员变量(那些不能被注入或者没有被注入的成员变量)。

public void afterPropertiesSet() throws Exception {
if (this.definitionLocation == null) {
throw new FatalBeanException(“Property [definitionLocation] of class [”
+ ProcessDefinitionFactoryBean.class.getName()
+ “] is required.”);
}

InputStream inputStream = null;
try {
inputStream = this.definitionLocation.getInputStream();
this.processDefinition = ProcessDefinition.parseXmlInputStream(inputStream);
//       这里就是初始化的地方,spring的配置文件中只配置了definitionLocation的信息,其他的事就在这里做

}
finally {
if (inputStream != null) {
inputStream.close();
}
}
}

  • FactoryBean.getObject & getObjectType

在比较配置文件和源代码的时候我很疑惑,为什么类型都不对头,怎么可以注入的?答案就在FactoryBean的这两个方法里

public Object getObject() throws Exception {
return this.processDefinition;
}

public Class getObjectType() {
return (processDefinition == null) ? ProcessDefinition.class : processDefinition.getClass();
}

getObject返回的是成员变量processDefinition,getObjectType返回的也不是this.class,而是processDefinition.getClass();spring在加载这样的factorybean的时候就会根据这两个方法来决定类型和实例。比如在xml中配置了ref=”someFactoryBean”的 时候,实际的引用可以是任意的,只要你提供了这两个方法。-好像有点表达不清楚。

xmpp的验证流程

来自 www.xmpp.org

Step 1: Client initiates stream to server:

 

<stream:stream
    xmlns='jabber:client'
    xmlns:stream='http://etherx.jabber.org/streams'
    to='example.com'
    version='1.0'>

Step 2: Server responds with a stream tag sent to client:

 

<stream:stream
    xmlns='jabber:client'
    xmlns:stream='http://etherx.jabber.org/streams'
    id='c2s_234'
    from='example.com'
    version='1.0'>

Step 3: Server informs client of available authentication mechanisms:

 

<stream:features>
  <mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
    <mechanism>DIGEST-MD5</mechanism>
    <mechanism>PLAIN</mechanism>
  </mechanisms>
</stream:features>

Step 4: Client selects an authentication mechanism:

 

<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl'
      mechanism='DIGEST-MD5'/>

Step 5: Server sends a [BASE64] (Josefsson, S., “The Base16, Base32, and Base64 Data Encodings,” July 2003.) encoded challenge to client:

 

<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
cmVhbG09InNvbWVyZWFsbSIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixxb3A9ImF1dGgi
LGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNzCg==
</challenge>

The decoded challenge is:

 

realm="somerealm",nonce="OA6MG9tEQGm2hh",
qop="auth",charset=utf-8,algorithm=md5-sess

Step 5 (alt): Server returns error to client:

 

<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
  <incorrect-encoding/>
</failure>
</stream:stream>

Step 6: Client sends a [BASE64] (Josefsson, S., “The Base16, Base32, and Base64 Data Encodings,” July 2003.) encoded response to the challenge:

 

<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
dXNlcm5hbWU9InNvbWVub2RlIixyZWFsbT0ic29tZXJlYWxtIixub25jZT0i
T0E2TUc5dEVRR20yaGgiLGNub25jZT0iT0E2TUhYaDZWcVRyUmsiLG5jPTAw
MDAwMDAxLHFvcD1hdXRoLGRpZ2VzdC11cmk9InhtcHAvZXhhbXBsZS5jb20i
LHJlc3BvbnNlPWQzODhkYWQ5MGQ0YmJkNzYwYTE1MjMyMWYyMTQzYWY3LGNo
YXJzZXQ9dXRmLTgK
</response>

The decoded response is:

 

username="somenode",realm="somerealm",
nonce="OA6MG9tEQGm2hh",cnonce="OA6MHXh6VqTrRk",
nc=00000001,qop=auth,digest-uri="xmpp/example.com",
response=d388dad90d4bbd760a152321f2143af7,charset=utf-8

Step 7: Server sends another [BASE64] (Josefsson, S., “The Base16, Base32, and Base64 Data Encodings,” July 2003.) encoded challenge to client:

 

<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZAo=
</challenge>

The decoded challenge is:

 

rspauth=ea40f60335c427b5527b84dbabcdfffd

Step 7 (alt): Server returns error to client:

 

<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
  <temporary-auth-failure/>
</failure>
</stream:stream>

Step 8: Client responds to the challenge:

 

<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>

Step 9: Server informs client of successful authentication:

 

<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>

Step 9 (alt): Server informs client of failed authentication:

 

<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
  <temporary-auth-failure/>
</failure>
</stream:stream>

Step 10: Client initiates a new stream to server:

 

<stream:stream
    xmlns='jabber:client'
    xmlns:stream='http://etherx.jabber.org/streams'
    to='example.com'
    version='1.0'>

Step 11: Server responds by sending a stream header to client along with any additional features (or an empty features element):

 

<stream:stream
    xmlns='jabber:client'
    xmlns:stream='http://etherx.jabber.org/streams'
    id='c2s_345'
    from='example.com'
    version='1.0'>
<stream:features>
  <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
  <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
</stream:features>

MoBarbr – 记账

这个是前一阵子有空的时候用swing写的一个简单的记账软件。很简单的功能,无非就是记账,添加顾客,充值,修改,报表。用Spring来管理swing ui组件之间的依赖,发现也很方便,而且看起来更清晰,不像以前那种panel,frame,dialog的引用传来传去。本来还准备用thinlet重写一遍,后来因为thinlet只支持awt而没做。同时还试了一些方法来发布这个桌面应用,见Java桌面应用程序发布 截图: 下载 源代码

使用canvas画的一个登录的Form

<!–code { font-family: Courier New, Courier; font-size: 10pt; margin: 0px; }– 使用canvas也就是通常说的低级组件(low-level user interface api)画了一个登录的form,这个用high-level的form很简单,但是自己写确实很麻烦。因为所有的控制都要自己写:键盘输入(输入框),组件绘制,动画线程(光标闪烁), 输入框长度控制, 组件焦点, 密码mask. 刚刚开始接触wtk,练习一下.

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
public class LoginForm extends Canvas implements CommandListener {

private Image img;
private Font font;
public int currentPosX, currentPosY;
public int width, height;
public int inputBoxWidth = 150;

public int offset = 5;
public int curX = 0, curY = 0;

static final int USER_INPUT = 1, PASS_INPUT = 2, REMEMBER_CHECK = 3;

public String username = “”, password = “”;
public String usernameShow = “”,passwordShow=“”;//the display part of these two fields
public int userInputLimitation = 0,passInputLimitation = 0; //characters limitation of input box

public int userInputX, userInputY;
public int passInputX, passInputY;
public int remeCheckX, remeCheckY;
public int curSel = USER_INPUT;

public int cursorX,cursorY;
public int cursorColor = 0x00000000;
public CursorBlink cursorBlink;

private StringBuffer input = new StringBuffer(“default input”);
boolean initialized = false;

public LoginForm() {
setCommandListener(this);
width = getWidth();
height = getHeight();
// The current cursor position for the text
currentPosX = width / 2;
currentPosY = height / 2;
// The text font
font = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN,
Font.SIZE_LARGE);

// setFullScreenMode (true);
// Cursor blink thread
cursorBlink = new CursorBlink(this);
Thread blinkThrd = new Thread(cursorBlink);
blinkThrd.start();

setCommandListener(this);

try {
img = Image.createImage(“/imgs/” + “gtalk.jpg”);
} catch (Exception e) {
e.printStackTrace();
img = null;
}
}

protected void paint(Graphics g) {
if (!initialized) {
// Fill a white background
g.setColor(0xffffff);

g.fillRect(0, 0, width, height);
// Draw logo image
g.drawImage(img, curX, curY, Graphics.TOP | Graphics.LEFT);

// Draw form
// set font and color-black
g.setFont(font);
g.setColor(0, 0, 0);
// Draw name label and box
curY += img.getHeight() + offset;
g.drawString(“Username “, curX, curY, Graphics.TOP | Graphics.LEFT);
curY += offset + font.getHeight();
userInputX = curX;
userInputY = curY;
g.drawRect(curX, curY, inputBoxWidth, font.getHeight());
curY += offset / 2 + font.getHeight();
g
.drawString(“@gmail.com”, curX, curY, Graphics.TOP
| Graphics.LEFT);
// Draw password label and box
curY += offset + font.getHeight();
g.drawString(“Password “, curX, curY, Graphics.TOP | Graphics.LEFT);
curY += offset + font.getHeight();
passInputX = curX;
passInputY = curY;
g.drawRect(curX, curY, inputBoxWidth, font.getHeight());

cursorX = userInputX;
cursorY = userInputY;

initialized =true;
}else{
update(g);
}

}

public void update(Graphics g) {
highLightSelection(g);
//remove previous input
g.setColor(255, 255, 255);
//1px offset for the 1px border
g.fillRect(userInputX+1, userInputY+1, inputBoxWidth-1, font.getHeight()1);
g.fillRect(passInputX+1, passInputY+1, inputBoxWidth-1, font.getHeight()1);
//draw the input again
drawInput(g);
showCursor(g);
}

private void drawInput(Graphics g) {
g.setColor(0,0,0);
g.setFont(font);
usernameShow = username;
//add a blank space to avoid the char cross line, as the space available is not enough for a char like “k”, but enough for “l”
if(font.stringWidth(usernameShow+” “) >= inputBoxWidth){
if(userInputLimitation == 0) userInputLimitation = username.length();
usernameShow = username.substring(username.length()-userInputLimitation);
}
passwordShow = mask(password);
if(font.stringWidth(passwordShow+” “) >= inputBoxWidth){
if(passInputLimitation == 0) passInputLimitation = password.length();
passwordShow = password.substring(password.length()-passInputLimitation);
}

g.drawString(usernameShow, userInputX, userInputY, Graphics.TOP
| Graphics.LEFT);
g.drawString(mask(passwordShow), passInputX, passInputY, Graphics.TOP
| Graphics.LEFT);
}

private void showCursor(Graphics g) {
g.setColor(cursorColor);
if(curSel == USER_INPUT){
g.drawLine(userInputX+font.stringWidth(usernameShow),userInputY+1,userInputX+font.stringWidth(usernameShow),userInputY+font.getHeight()1);
}else if(curSel == PASS_INPUT){
g.drawLine(passInputX+font.stringWidth(mask(passwordShow)),passInputY+1,passInputX+font.stringWidth(mask(passwordShow)),passInputY+font.getHeight()1);
}

}

private String mask(String pass){
StringBuffer sb = new StringBuffer();
int le = pass.length();
for(int i=0;i<le;i++){
sb.append(‘*’);
}
return sb.toString();
}

private void highLightSelection(Graphics g) {
// set blue background
g.setColor(0, 0, 255);
if (curSel == USER_INPUT) {
g.drawRect(userInputX, userInputY, inputBoxWidth, font.getHeight());
g.setColor(0, 0, 0);
g.drawRect(passInputX, passInputY, inputBoxWidth, font.getHeight());

} else if (curSel == PASS_INPUT) {
g.drawRect(passInputX, passInputY, inputBoxWidth, font.getHeight());
g.setColor(0, 0, 0);
g.drawRect(userInputX, userInputY, inputBoxWidth, font.getHeight());

}
}

public void keyPressed(int keyCode) {
int gameCode = getGameAction(keyCode);
if (gameCode == UP) {
curSel = USER_INPUT;
} else if (gameCode == DOWN) {
curSel = PASS_INPUT;
} else if (gameCode == LEFT) {
//TODO move cursor
} else if (gameCode == RIGHT) {
//TODO move cursor
} else if (keyCode == –8 || keyCode == 8) { // -8 clear key, 8 backspace
if (curSel == USER_INPUT) {
if(“”.equals(username))return;
username = username.substring(0, username.length()1);
} else if (curSel == PASS_INPUT) {
if(“”.equals(password))return;
password = password.substring(0,password.length()1);
}
} else {
char c = (char) keyCode;
if (curSel == USER_INPUT) {
username += c;
} else if (curSel == PASS_INPUT) {
password += c;
}
}
repaint();
}

public void commandAction(Command c, Displayable d) {
System.out.println(c);
}

class CursorBlink implements Runnable {
LoginForm form;

public CursorBlink (LoginForm form) {
this.form = form;
}
public void run () {
int seed=0;
while (true) {
seed++;
if( seed%2 == 0){
cursorColor = 0x00ffffff; //white
}else{
cursorColor = 0x00000000; //black
}
form.repaint();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}


jBPM中的assignment

ZhangV

在jbpm的流程定义文件(processdefinition.xml)中当定义一个task的assignment的时候有几种选择:

  • Swimlane
  • Pooled Actors
  • Expression
  • Actor
  • Handler

这里分别描述一下每种的用法,以及使用场景。使用场景的理解很重要,只有清楚每一种用法的所适合的情形,以及他们之间的相互转换才能说是真的“会用了”。

Swimlane
泳道是用来处理一个流程中的多个任务必须由同一个人来处理的机制。所以,当一个流程的第一个任务实例被创建到指定的泳道后,第一个任务实例中的执行人被保 存,该泳道中的后续任务的处理人都是第一个任务的处理人。所以一个泳道有一个人员被分配,并且泳道中的任务不需要再分配人员。这里是指这个流程中的参与者或者角色。在实际的业务系统中,他可能根本就不是一个业务角色,可能是一个外部应用,也可能是一个处理过程,或者是几个业务角色的结合。所以要清楚他是指“在该流程中”的角色。在jPDL的user guide中写的很清楚也反复强调 – “process role”。
通过在流程定义文件中定义:
<swimlane name=”leader”>
<assignment class=”com.modofo.engine.swimlane.LeaderSwimlane”></assignment>
</swimlane>
同样可以实现运行时的结合实际应用进行灵活的任务分配。这也是目前我看到的使用比较多的方式。

Actor
相当于设置TaskInstance的actorId属性,主要用于获取某个actor的所有任务列表。设置方法在定义中描述actor-id属性,或者调用TaskInstance.setActorId(String)方法,或者在AssignmentHandler中调用assignable.setActorId(String)方法。

Pooled Actors
用于定义所有的任务参与人(candidates for task instance)。在查找某个参与者可参与的任务时使用,其实就是一个actor的集合,jBPM通过TaskMgmtSession.findPooledTaskInstances(String actorId)或者TaskMgmtSession.findPooledTaskInstances(List actorIds) 来查找任务集合。
注意:actor的优先级比pooled actors要高,所以如果同时设置两者,那jBPM只会用actor来查找,而忽略pooled actors。

一句话: actor被用来获取某个参与者的个人任务列表,pooled actors用来获取某个参与者可以参与的所有任务列表,或者说组任务列表。
使用这个actor集合的初衷就是为了不让任务分配和角色体系耦合。流程定义中始终都是只有参与者的名称和集合,而不存在用户,角色和用户组的具体设计。而使用Expression就耦合了这些信息在流程定义里。

Expression
如果你使用jBPM的identity component的话。你可以使用表达式定义的任务分配模式。通常这个表达式的形式是:

<assignment expression=’previous –> group(hierarchy) –> member(boss)’ />
我看了之后也不是很清楚,group和member是jBPM的identity componenet的概念,这个表达的意思大概是:使用之前的actor,然后在hierarchy组,并且member(角色)是boss的所有actor。这种方法仅限于使用jBPM的IC时使用。

Handler

指定你自己的AssignmentHandler实现。该接口只有assign方法。这种任务分配方式主要用于运行时的任务分配,而不是hardcode到定义文件里。使用场景,如果不想让原本应用中的访问控制与jBPM耦合起来,就可以使用这种方法来将jBPM中的任务分配代理出来。当然更好的定义是用swimlane,这样可以让定义文件看起来不是很凌乱。

看板在行动(Kanban in Action)

作者:DavidAnderson 原文
翻译:ZhangV 2007-11-26

之前,在讨论我在corbis所使用的软件工程管理方法时 ,我曾提到引入看板系统来维持项目运行。因为它的引入,我们现在每个月平均发布两个新的版本。然而,这些并没有使用传统的敏捷开发方法如两周迭代(two week iteration)或快速迭代(sprint)。而是用看板系统来管理变更请求(CR-Change Request)。当CR被完成时,它被设在准备发布状态,并在每周三被发布。

尽管我们让没有用Visual Studio的开发人员使用了outlook的teamlook客户端联入Team Foundation Server跟踪所有CR,但基本上的日常工作,我们都是使用白板作为看板来跟踪所有CR,把Post-It当作看板卡。

我们的支持过程是由一组常见的软件工程资源驱动的,没有一个专职的支持维护团队。通过设置看板,我们可以向管理层保证 – 我们正在履行我们的承诺,我们使用了一定的资源来进行支持活动,而不需要特别指出是谁在负责。

每天早会上所有团队成员会围绕看板来进行当天的工作安排,讨论进度和当前存在的问题。达伦戴维斯,作为项目经理,会主持会议,并分析每张卡片并与其他成员讨论。每张卡上写着CR的标题以及ID,及负责人的名字。每个负责人有责任根据当前CR的进行阶段移动相应CR的卡片。达伦利用每天的早会来保证Team Foundation Server上的数据和看板是同步的。该看板系统基本上是自我组织(self organizing),但整个团队每天都会验证它的有效性。

一些关键概念。红星 – 这个任务已经严重超时的,超过28个工作日服务水平协议( SLA),通过该系统。这使”迟到”的项目能够自我加速,不需直接管理干预。粉红色卡表示这个CR在Team Foundation Server里还是open的。这些粉红色卡包含了描述信息和在Team Foundation Server里的ID 。还有一些其他类型的卡片:黄色-客户提出(customer-valued)的CR;蓝色-客户提出(和要求)的BUG;绿色-IT维护项,如:的sql 2005的升级;橙色-新增(或额外)的BUG;粉红-issue。

看板限制卡片最多只能有20张,分为不同阶段,系统的分析,开发,测试,构建/合并,部署。此外,我们也允许有3个bug可以放在任何地方。这样可以有效地利用闲散资源。当人手不够的时候,我们会减少这个数量,或者干脆不要。此外,还有两个维护项,这让我们能解决固定分配一些资源到大大系统维护和升级,例如API版本升级,与平台的升级。 如net2.0或sql server 2005 。

看板赋予我们三个成功要素:

  • 减少“进行中(work-in-progress)”, (讽刺的是,这些事情本身会限制工作的完成)
  • 量入而出(如果有新的CR,首先看看板上有没有足够的空间)
  • 确定优先顺序

我们每周和公司的技术总监开会讨论,决定任务的优先等级。他们从后台日志中拿出新的CR并考虑怎么把他们配置到看板上。这迫使他们思考哪些是最重要的事情。看板强迫他们给出优先等级。

看板还把我们从定时迭代的压迫(constraints of time-boxed iterations)中解放出来。尽管我们现在每两个星期发布一个新版本,大的任务可以在系统中最多可以存在60天。那些对于“一(两)周迭代”来说太大的任务同样可以放到看板里,而不需要特殊对待。

最后,看板解放了我们,我们不再把每个迭代都当作一个小型的项目而投入过多的管理成本它基本上是自行组织和管理,除非有特殊情况出现,如需要加快请求或因环境或系统的维修问题变更发布时间。

technorati的标签:敏捷,朱+安德森,精益生产,看板,软件+工程

顺便问一下customer-valued怎么翻译?

最后修改:2009-8-12