狂奔 lion

自强不息

OA的杀手级应用

在远古时期人们靠结绳纪事,据说美洲的玛雅文明在覆灭之前都一直没有自己的文字,而采用这种古老的方法。
后来我们的祖先发明了文字,在竹简上,布帛上书写文字,竹简和布帛就是信息的载体,这样的载体造价不菲,所以我们的文言和白话就有这么大的差距,留下的论语也要微言大义。再后来我们的祖先发明了纸张,严重地降低了承载信息的开销,于是人类的文明得以更好地记录和更快地发展。今天,我们的信息载体又有了新的变化,一张光盘,一个硬盘都可以承载无数的学问。
信息有了载体,随之产生了信息管理的问题:
如何对信息进行增删改查的操作?如何处理附加在信息上的工作流?如何管理权限?
在IT系统出现之前,人们通过图书馆管理图书,通过搬运工进行信息流动,通过钥匙和锁头管理权限。
在IT系统出现之后,人类可以通过计算机来完成这些操作。这其中数据库系统,工作流系统帮了我们大忙。
IT系统处理信息的过程是:
多样式的文档(抽象为XML)-> 内存数据 -> 持久化(文件数据库)->内存数据 -> 多样式的文档(抽象为XML)
我们让用户在我们设定的UI窗口进行输入,然后通过报表的形式产生各种各样的输出。中间一个步骤一个步骤,一个环节一个环节的走下去。
这其中,我们把很大一部分精力消耗在,如何让用户舒服的输入,和产生用户所需要的输出上。
于是人们就要思考,难道不可以直接编辑一种全世界认可的文档形式(大家现在正在和MS争吵的东西)通过网络流转,然后获得结果吗?为什么要从程序员的角度加给用户数据库,工作流,录入界面这些繁杂的东西?
从这点意义上说,我推崇Sharepoint或者Google sites。
就拿google docs来说吧,在线编辑word,每个环节通过分享的形式进行编辑,授权这部分可以通过历史记录来找到,随时恢复到历史版本,因此也不怕误操作和无权限操作,真正的所见即所得,支持50人同时在线编辑。这难道不是OA的杀手级应用吗?

posted @ 2008-03-05 13:31 杨一 阅读(1234) | 评论 (1)编辑 收藏

如何学习spring

学习这些框架技术,我觉得归根结底就是做什么的,为什么做,如何做
前人说读书有三个层次,我看这大概可以总结为是新的三个层次:)
因为没有搞清楚为什么要用,就会误用,用了还不如没用。其实我觉得学spring读读rod那个原著挺好的,比单纯学spring有帮助,最好自己有体会。比如你开发网站很熟练了,自然就知道为什么要用spring了。等完全领会了他那两本书后,再读读他们的reference book应该差不多了。
这个过程其实就是做什么->为什么->怎么做的过程

posted @ 2008-01-16 10:19 杨一 阅读(1005) | 评论 (1)编辑 收藏

系统模型及系统故障日志的思考

最近在研究关于系统的基于日志的故障恢复,无意间在网上发现一篇论文中对于系统日志模型的精彩论述,翻译过来并附上我的思路:

一个系统是一个具有明显的边界的实体,它根据一定的输入,自身运行逻辑及系统的内部时钟变化来产生相应的输出。
所谓“明显的边界”是指系统所产生的输出是明确而无二义性的。我们称这个边界为系统的设计规范(specification)。一个系统通过与其所处环境进行交互,从而获取输入并产生输出。一个系统可以被拆解为不同的子系统。这些子系统通常被称为系统模块(system components),每个模块又独立地成为一个系统,作为一个系统,这个模块又会和它的相关环境进行交互(比如,一个更大的系统中的其他的模块组件)来获取输入并产生输出,这些模块还可以继续被分解为更小的子系统。
一个系统可以被建模为一个状态机(state machine),其中的状态包含了系统所持有并处理的数据。这些状态的迁移被分为两大类:由系统内部逻辑所触发且对外部环境透明的迁移和直接与外部环境相接触的迁移。前者的例子如内存数据和寄存器数据的转换,内存中数据结构的重组。第二种迁移的例子包含了各种各样的系统和环境之间的交互,一般来说,如果这个过程能被建模成系统的I/O操作,则应属于这一类别。因此,一个消息内容的形成是一个或多个第一类别状态迁移的结果,但将消息输出到系统的环境则是属于第二类迁移。
第二类别的状态迁移可以捕获交互事件(interaction events),或者简单的事件(events)。这些事件可以由系统外部的观察者(observer)来获取。显然,这里的事件是消息、信号、数据及其内容以及一切系统和其环境交互(如机器人运动手脚,报警器报警,打印机打印等等)的发送和接受的模型。此外事件还可以用来描述系统缺乏交互的时间,比如一个计时器在日志中输出系统的空闲时间等。
当一个大的系统被拆分成多个模块时,每个模块都被赋予了整个系统所处理数据的一部分,正因为模块和模块间的接口衔接和数据感知,一些原来属于第一类别的状态转换,因为系统的拆分在更低的层次上变成了第二类别,成为系统和环境之间的交互。
对于一个特定的系统,他对于输入的形式和获取时间是不可预知的,但是这个系统却应该能够做到根据一个特定的输入以及系统当前的特定状态获取一个特定的输出。因此系统的执行可以被建模为状态转换序列,每个状态的输入是一个不确定性事件。为了记录日志并做到故障恢复,我们还应做到能够在环境中捕获这个不确定性事件输入。
此外,在系统与系统间进行交互式,事件的传递时间也应该是不确定性的。



怎样用日志来预防系统崩溃,在崩溃后如何还原系统,我想关键问题就是怎么做好内存的快照,这样,在断电重启后可以通过日志来还原内存的信息这样第一步就是确认内存中的数据结构,哪些是必不可少的。确定系统恢复的粒度,按照子系统的分割和事件的记录来进行replay,根据子系统的划分,可以找出每个子系统中第二类别的事件进行记录。
以向数据库系统提交作业为例,实际上在整个作业提交的过程中,每个层次都要做到可以在失败的情况下重现,这个功能在完善的数据库系统和集群批处理系统中当然已经很完善。但如果是针对web系统的作业提交,则需要针对Web的作业持久方案,做一个日志恢复处理。需要特别指出的是,对于数据的查询是不需要做备份的。
在具体实现上,我想应该包括:日志记录,故障检测,日志持久三个部分。一份日志就是一个对于系统检查点(checkpoint)的连续记录。日志记录者负责记录日志到日志持久者,故障检测器随时监控系统,发现故障后,从日志持久者中读取日志,进行replay.

posted @ 2008-01-07 14:44 杨一 阅读(967) | 评论 (0)编辑 收藏

如何实现包含插件功能的Applet Web界面

不知诸位有没有想过用Applet来组织Web的程序界面?小弟最近整理了一些杂碎的思路,思想完全开放,欢迎批评。
先说一下可能遇到的问题:
1 安全性:Applet对本地资源的操作需要相应的安全许可;
2 库资源的下载:如何下载及管理支持本地Applet的库资源;
3 通信:Applet如何与后台的Servlet进行通信;
4 图形的加载:如何利用Applet动态的实例化并展现界面。

下面一一展开讨论

(一)保障安全性

安全性的主要解决方案是利用Java提供的keytool生成一个keystore,并利用这个keystore对jar包进行signjar的操作。
整个对Java文件的编译,打包和signjar过程可以利用Ant来完成,比如下面的Ant脚本片段就是用来处理signjar的,大家也可以通过相应的Java命令直接处理:

< target  name ="signjar"  depends ="jar" >
 
< signjar  jar ="example.jar"
  keystore
="${basedir}/yangyi.keystore"  storepass ="mypassword"  alias ="mykey" ></ signjar >
</ target >

如果直接用命令,则其形式为:
jarsigner [ options ] jar-file alias
具体的操作方法可以参考下面的链接:
http://java.sun.com/j2se/1.5.0/docs/tooldocs/windows/jarsigner.html
通过这个signjar的操作,我们就给这个包中的类进行了一次数字签名,这样,当Applet进行默认许可之外的操作时,窗口将弹出这个数字签名要求本地用户进行确认,只有在确认后,用户才可以继续进行操作。

这样我们可以在第一次用户运行Applet程序时,在用户的许可下动态地在用户的$user.home/.java.policy下生成一个授权文件,以后就可以执行需要的操作了,为保证客户端的安全性,仅赋予用户执行特定文件夹权限的权利,如(仅作为例子,可以根据需要自己配置文件和Socket访问权限):
grant codeBase "file:/home/yiyang/test/×" {
 java.security.AllPermission;
};

(二)下载并管理库支持

这个过程可以通过Java的URL类提供的openConnection方法来获取一个InputStream从而获取到远程的资源(包括支持库和配置文件两部分)
1)对于配置文件,因为其内容都比较少,而且比较简单,可以直接通过输入流来获取,这是没有异议的;
2)对于库文件,在下载之前先到我们管理的库的目录下找到版本索引文件(我们设定一个版本索引文件来管理升级),这个版本索引文件如下所示:

time={资源获取的时间戳}
lib1.jar=1.0
lib2.jar=1.1

其中,服务器端也保留有一份这样的版本文件,当下载库文件时,首先对客户端和服务端的库的总时间戳进行比较,如果客户端大于或等于服务端,则不需下载,否则,如果客户端对应项目为空或者其总的时间戳小于服务端,则进一步比较内部库文件的版本,发现版本低的库或在客户端不存在的库后,自动到服务器上进行下载,在下载成功后,更新客户端的索引文件。

(三)通信

这个问题小弟曾在以往的blog中有过详细的讨论,可以到http://yangyi.blogjava.net中的相应随笔中找到答案。总的来说,在类型协议并不复杂,且客户端,服务端均为Java开发的情况下,应用Hessian是一个好的解决方案,需要指出的是Hessian中的代码对客户端来说并不是全部必须的,大家可以根据客户端的使用情况对这个库进行瘦身。只保留作为客户端必要的类即可。

(四)动态的实例化及插件结构

我们要实现用户界面的集成,从根本上说要解决下面的几个问题:
1)菜单集成
2)支持库集成
3)集成点
4)输出变量
对于客户端为Applet开发的插件,我们把上面的四项配置统一在XML文件中进行描述定义。
这里需要注意的是菜单要提供名称,支持库要提供下载路径或者本地路径,集成点我们希望是一个JPanel。
在定义好XML后,可以到网址:http://www.flame-ware.com/xml2xsd/去获得一个对应的schema,利用这个schema和JAXB提供的xjc工具,我们就可以生成对应的XML操作类,来对配置进行处理。
对于菜单的集成可以动态地在JMenu中添加MenuItem(表示插件的功能)
根据配置的支持库的位置,我们可以通过Java的URLClassLoader对库进行动态的加载,然后根据相应的集成点,获取实例,这个过程的示例代码如下所示:

File f  =   new  File( " a.jar " );  // a.jar是我们从配置文件中读取的支持库
URL url  =   null ;
Class lib 
=   null ;
try   {
 url 
=  f.toURI().toURL();
 lib 
=  Class.forName( " Lib " true new  URLClassLoader(
   
new  URL[]  { url } ));  // Lib是我们从配置文件中读取的集成点
 JPanel plugin_panel  =  (JPanel)lib.newInstance();
 
return  plugin_panel;
}
  catch  (Exception e)  {
 e.printStackTrace();
}

对于输出变量,其主要作用是用户各个插件之间的信息交互,我们可以通过一个总的HashMap来实现,为避免变量值出现冲突,在变量名前自动加上插件名的前缀。
如plug_in1的变量var1,其系统名称为:plug_in1__var1.

解决了上面的四个障碍,我们就可以把精力投入到具体的功能实现上了。

posted @ 2008-01-02 15:07 杨一 阅读(1705) | 评论 (4)编辑 收藏

好好学习,天天向上

总算找到了工作. 我会好好努力的. 此时此刻,更加明白,以往的一切荣与辱都成为过去. 又要踏上新的征程.

posted @ 2007-12-30 12:56 杨一 阅读(286) | 评论 (0)编辑 收藏

myeclipse

刚看了myeclipse,eclipse是一个很可怕的东西,它试图让所有的开发人员一打开电脑就不能够离开它,还要在里面完成所有的工作。人们不至于反感它的原因是它是开源的,不受商业控制的。如果我们对于myeclipse过度依赖,必然最终走向对微软严重依赖的老路。我不反对利用软件盈利。但是自由的精神不应被改变。
微软和我们是原始的猎人与猎物之间的关系,虎与伥的关系,最终极的占有。我们这才生是MS的人,死是MS的鬼。

posted @ 2007-12-28 11:39 杨一 阅读(305) | 评论 (0)编辑 收藏

利用JAAS及JNI实现在Java环境下的Unix/Linux权限认证

     摘要: 这篇随笔谈一谈如何在Java环境下利用Unix/Linux的用户名和密码对用户的权限作出过滤。为方便大家学习交流,本文中给出了源代码,借此抛砖引玉,欢迎大家对这个简单的登录模型做出改进或者设计出自己的技术方案。
由标题我们不难看出,与本文相关的知识点主要有3个:
1 JAAS这个解耦设计的多层验证方法(1.4后已归入Java核心库中)
2 应用JNI访问底层代码,及JNI中简单的类型匹配
3 在shadow模式下,Unix/Linux系统的用户验证  阅读全文

posted @ 2007-12-12 16:50 杨一 阅读(1424) | 评论 (0)编辑 收藏

延迟加载技术及其在iBATIS中的实现

O/R映射框架的延迟加载技术实现大体上有这么4种(参看Martin Fowler的意见):
(http://www.martinfowler.com/eaaCatalog/lazyLoad.html)

There are four main varieties of lazy load. Lazy Initialization uses a special marker value (usually null) to indicate a field isn't loaded. Every access to the field checks the field for the marker value and if unloaded, loads it. Virtual Proxy is an object with the same interface as the real object. The first time one of its methods are called it loads the real the object and then delegates. Value Holder is an object with a getValue method. Clients call getValue to get the real object, the first call triggers the load. A ghost is the real object without any data. The first time you call a method the ghost loads the full data into its fields.

通过阅读源代码,发现iBATIS中的延迟加载是用上述方式中的虚拟代理实现的.

在动态代理的实现上, iBATIS有Java动态代理和CGLIB两种实现方案,iBATIS把用CGLIB实现的方案称为Enhanced的方案,可见CGLIB的效率会比java的动态代理效率要高.
在iBATIS首先判断是否定义了延迟加载,如果定义了,则利用Lazy的Loader来提取数据(返回一个Proxy).如没有执行对这个的任何操作,或者只是不再使用(finalize),则不做处理,否者就加载真正的对象.

可以通过阅读类
com.ibatis.sqlmap.engine.mapping.result.loader.LazyResultLoader
的源码获取更多的细节.

posted @ 2007-12-09 19:17 杨一 阅读(2036) | 评论 (0)编辑 收藏

浅谈Java中的通信机制及与C/C++ API的集成(下)

接着上次的话题,今天我们聊聊gSOAP这个框架,我们把用C写的旧有系统用gSOAP改造一下,通过SOA的形式发布出去。
上文提到,利用gSOAP可以做到以下3点:
1 一个Stand-alone的服务器外壳
2 一个根据API程序自动生成的Web Services服务
3 一个WSDL描述符文件

客户根据 WSDL 描述文档,会生成一个 SOAP 请求消息。Web Services 都是放在Web服务器后面,客户生成的SOAP请求会被嵌入在一个HTTP POST请求中,发送到 Web 服务器来。Web 服务器再把这些请求转发给 Web Services 请求处理器。请求处理器的作用在于,解析收到的 SOAP 请求,调用 Web Services,然后再生成相应的 SOAP 应答。Web 服务器得到 SOAP 应答后,会再通过 HTTP应答的方式把信息送回到客户端。
WSDL是Web服务中客户端和服务端沟通的桥梁,描述了对象提供的方法。SOAP帮我们制定了一份被官方认可的对象的封装方法。有了WSDL,客户端只关心如何把参数用Soap封装起来发出去,并获取结果。服务端只关心如何对Soap进行拆包->服务->封包。gSOAP可以帮我们实现上述过程中的拆包和封包,而我们可以只关心服务的实现。

言归正传,在这里我们以一个简单的实现加、减、开放的Web Services的服务为例子,介绍gSOAP的使用:
为了发布这个Web服务,首先我们需要把服务的接口定义好,这个服务可能是一个现有服务的Adapter,为此我们定义头文件
calc.h:
typedef double xsd__double;
int ns__add(xsd__double a, xsd__double b, xsd__double &result);
int ns__sub(xsd__double a, xsd__double b, xsd__double &result);
int ns__sqrt(xsd__double a, xsd__double &result); 
注意到这里面我们把double定义成了xsd__double(两个下划线),这是为了告诉gSOAP,我们需要的soap格式和WSDL格式是基于Document/literal的而非rpc/encoded.为了不把事情搞复杂,在这里我只能说,Java1.6自带的Web Services工具只支持Document/literal格式的WSDL,所以我们生成这种格式的WSDL。至于这两种格式之间选择和他们的long story,大家可以参考下面的文章:
http://www.ibm.com/developerworks/webservices/library/ws-whichwsdl/
编写好头文件后,我们就可以利用gSOAP提供的工具进行生成了:
/usr/lib/gsoap-2.7/bin/soapcpp2 -S -2 calc.h
生成的主要文件详见附件。
下面我们实现calc.h中定义的函数:
// Contents of file "calc.cpp": 
#include "soapH.h" 
#include 
"ns.nsmap" 
#include 
<math.h> 
int main()
{
   struct soap soap;
   
int m, s; // master and slave sockets
   soap_init(&soap);
   m 
= soap_bind(&soap, "localhost"9999100);
   
if (m < 0)
      soap_print_fault(
&soap, stderr);
   
else
   {
      fprintf(stderr, 
"Socket connection successful: master socket = %d\n", m);
      
for (int i = 1; ; i++)
      {
         s 
= soap_accept(&soap);
         
if (s < 0)
         {
            soap_print_fault(
&soap, stderr);
            
break;
         }
         fprintf(stderr, 
"%d: accepted connection from IP=%d.%d.%d.%d socket=%d", i,
            (soap.ip 
>> 24)&0xFF, (soap.ip >> 16)&0xFF, (soap.ip >> 8)&0xFF, soap.ip&0xFF, s);
         
if (soap_serve(&soap) != SOAP_OK) // process RPC request
            soap_print_fault(&soap, stderr); // print error
         fprintf(stderr, "request served\n");
         soap_destroy(
&soap); // clean up class instances
         soap_end(&soap); // clean up everything and close socket
      }
   }
   soap_done(
&soap); // close master socket and detach environment
}
// Implementation of the "add" remote method: 
int ns__add(struct soap *soap, double a, double b, double &result) 

   result 
= a + b; 
   
return SOAP_OK; 

// Implementation of the "sub" remote method: 
int ns__sub(struct soap *soap, double a, double b, double &result) 

   result 
= a - b; 
   
return SOAP_OK; 

// Implementation of the "sqrt" remote method: 
int ns__sqrt(struct soap *soap, double a, double &result) 

   
if (a >= 0
   { 
      result 
= sqrt(a); 
      
return SOAP_OK; 
   } 
   
else
   { 
      
return soap_sender_fault(soap, "Square root of negative value""I can only compute the square root of a non-negative value");
   } 
前文提到过,我们不希望为了发布基于Web Services的C语言的API而开发或应用一个大的Web服务器。我们代码中的main函数实现了一个最简单的Web Server(基于Socket).这个Server利用gSOAP生成的API来提供针对SOAP的处理。
下面我们把这个嵌入式的web server编译,编译的时候注意stdsoap2.cpp这个文件是从gSOAP包中拷贝而来,不是自动生成的,大家下载gSOAP后直接就能找到这个文件及其头文件。
g++ -o calcServer calc.cpp soapC.cpp soapServer.cpp stdsoap2.cpp
一个以Web Servers形式提供的C API诞生了。
在server端执行./calcServer

下面讨论如何用Java1.6的自带工具生成一个客户端stub:
把gSOAP生成的WSDL拷贝到我们的Java开发环境中来,按照Web Services Server中定义的端口和服务器,配置参数生成客户端Web Services代码:
/usr/lib/jvm/jdk1.6.0_03/bin/wsimport -extension -httpproxy:localhost:9999 -verbose ns.wsdl

生成后,把这个环境添加到eclipse的编译环境中来,然后在eclipse中建一个新的类:
class Test {
    
public static void main(String args[]) {
        Service service 
= new Service();
        
double h = service.getService().sub(200001);
        System.out.println(h);
    }
}
运行后得到结果19999.0

总结:当集成Java和C两种平台时,我们可以有多种解决方案,但首先我们应该想到gSOAP因为它能够很出色地完成任务。
文中相关代码:
http://www.blogjava.net/Files/yangyi/gsoap.zip
广告:本人明年毕业,正在找工作,个人简历:
http://www.blogjava.net/Files/yangyi/My%20Resume.zip

posted @ 2007-12-06 12:19 杨一 阅读(1745) | 评论 (2)编辑 收藏

浅谈Java中的通信机制及与C/C++ API的集成(上)

背景:
对于旧有系统的改造和升级,最苦恼的莫过于跨平台,跨语言。我的一个朋友最近从Java专向了专攻.NET——因为.NET的CLR既有类似Java虚拟机概念这种已经被证明很成功的底层托管能力。又对于Windows的就有桌面应用提供了良好的兼容。
最近我的一个个人项目也面临着这样的需求。一个C语言开发的中间件,通过API暴露给二次开发及插件应用。现在由于对其应用的需求变得日趋复杂,而且正在脱离Unix的管理环境,走向基于JWS这样的BCS管理。有朋友推荐我用JNI,但这样一是增加了耦合度,二是让Java睡在JNI感觉不太安稳。在认知了上下两层的系统平台后,问题变得明朗起来:如何在HTTP协议下实现Java和C之间的交互?
思路:
本人对Java比较熟悉,先从Java的角度入手,Java间的通信方法:
1 通过URL,Applet/JWS访问被影射到URL的动态资源(Servlet)
2 通过URL,Applet/JWS访问共享的静态资源(Server定期更新静态资源)
3 通过序列化和反序列化,实现简单对象的传输(比如Resin的Hessian框架就提供了这种通信的方式)
4 通过一些工具做代码生成,利用Web Services实现客户端和服务端的交互
此外脱离HTTP,还可以做RMI,socket编程

现在问题是通信的一端由Java变成了C/C++, 于是, 解决方案1需要把动态资源由CGI来定义,而方案3变得不再适用。于是方案有:
1 通过URL,Applet/JWS访问被影射到URL的动态资源(CGI)
2 通过URL,Applet/JWS访问共享的静态资源(Server定期更新静态资源)
3 通过一些工具做代码生成,利用Web Services实现客户端和服务端的交互(×××这是我们讨论的重点×××)

解决方案:
现在针对上文提出的3中通信方式中的1和3谈一谈实现的方法,2的实现方案比较灵活,需要发挥大家的想象力了:)
针对CGI:
首先CGI可以配置在各种主流的服务器中作为后端的脚本运行。大家可能对Servlet更熟悉一些。
CGI可以用脚本写,也可以用C来实现。CGI被触发后,通过系统的环境变量来获得输入,在处理完毕后向标准输出中输出结果。
由此可以想见,Web服务器在接受到来自HTTP协议的请求后,首先把请求的参数获取到,然后设置到环境变量里。
根据对访问的URL的解析和服务器自身的配置,找到服务于请求的CGI程序的位置,然后执行这个程序。
这个程序被执行后通过环境变量得到了服务器先前设置在环境变量中的参数。在经过一些复杂的逻辑操作后,向标准输出输出结果。
这个输出又被Web服务器所捕获,转而传递回请求的客户端。
更多关于CGI的知识和理解,大家可以通过google来寻找答案

上述CGI的方式可以让我们直接获取到结果,但是方案比较原始和基础。其缺点有:
1 需要自己制定类型传输协议,做封装和拆封,否则只支持字符串
2 我们不会为了要用C的API就给它装一个或者自己实现一个Web服务器的,这让我们的底层程序显得蠢笨而冗余。我们希望能有一个超薄的Server外壳,
在对API封装后,通过某个端口进行开放即可。

针对Web Servcies:
Based on上面的两个不足,我们只能把希望寄托在Web Services身上了,
笔者在这里推荐给大家的是在C/C++很著名的Web Services工具gSOAP。大家可以到http://gsoap2.sourceforge.net/上去下载这个工具。
通过这个工具,我们可以做到:
1 一个Stand-alone的服务器外壳
2 一个根据API程序自动生成的Web Services服务
3 一个WSDL描述符文件

有关基于gSOAP的Web Services C服务端和Java客户端的运行机理,及通过Java客户端访问gSOAP的Web Services的过程中需要注意的问题(笔者费了一天周折才搞清楚),将在下一篇中描述

广告时间:
本人是哈工大的研究生,明年7月毕业。正在找工作,如果有工作机会,别忘了通知小弟哦(contactyang@163.com)

posted @ 2007-12-04 17:33 杨一 阅读(2013) | 评论 (0)编辑 收藏

仅列出标题
共6页: 上一页 1 2 3 4 5 6 下一页 
<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

导航

公告

本人在blogjava上发表的文章及随笔除特别声明外均为原创或翻译,作品受知识产权法保护并被授权遵从 知识分享协议:署名-非商业性使用-相同方式共享 欢迎转载,请在转载时注明作者姓名(杨一)及出处(www.blogjava.net/yangyi)
/////////////////////////////////////////
我的访问者

常用链接

留言簿(5)

随笔分类(55)

随笔档案(55)

相册

Java

其他技术

生活

最新随笔

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜

自强不息


用心 - 珍惜时间,勇于创造