Monthly Archives: December 2011

Maven的Snapshot版本与Release版本

1. Snapshot版本代表不稳定、尚处于开发中的版本

2. Release版本则代表稳定的版本

3. 什么情况下该用SNAPSHOT?

    
协同开发时,如果A依赖构件B,由于B会更新,B应该使用SNAPSHOT来标识自己。这种做法的必要性可以反证如下:

      a.如果B不用SNAPSHOT,而是每次更新后都使用一个稳定的版本,那版本号就会升得太快,每天一升甚至每个小时一升,这就是对版本号的滥用。

      b.如果B不用SNAPSHOT, 但一直使用一个单一的Release版本号,那当B更新后,A可能并不会接受到更新。因为A所使用的repository一般不会频繁更新release版本的缓存(即本地repository),所以B以不换版本号的方式更新后,A在拿B时发现本地已有这个版本,就不会去远程Repository下载最新的B

4. 不用Release版本,在所有地方都用SNAPSHOT版本行不行?     

     不行。
正式环境中不得使用snapshot版本的库。 比如说,今天你依赖某个snapshot版本的第三方库成功构建了自己的应用,明天再构建时可能就会失败,因为今晚第三方可能已经更新了它的snapshot库。你再次构建时,Maven会去远程repository下载snapshot的最新版本,你构建时用的库就是新的jar文件了,这时正确性就很难保证了。

个人必装软件和必须做的设置

同时使用Ubuntu和Windows:

1.Ubuntu下的五笔字型, Windows下的极点五笔

2.Firefox的插件:

  a.Autoproxy

  b.Firebug

  c.HttpFox

  d.Remove Cookies

4.Windows下的7zip

5.Windows下的staruml

6.Ubuntu下的gSTM + Firefox Autoproxy 插件

6.1 设置你的vps的SSH超时时间:  

  vi /etc/ssh/sshd_config

  ClientAliveInterval 60

  ClientAliveCountMax 1440

7.Ubuntu下的jdk下载安装及环境变量配置

8.Ubuntu下的chrome

9.Ubuntu下的defuse或meld

10. Ubuntu下的fqterm

11.用VNC让Ubuntu和Windows互联

  a.Ubuntu设置好“允许远程登录”,再安装RDSC用作VNC Client

  b.Windows下安装UltraVNC,包括Client和Server

  c.windows下安装Insomnia,让windows无法休眠,也就让windows上的vnc server无法休眠

12.用FreeNX从Windows连Ubuntu

  a.Ubuntu安装FreeNX服务端,可参照Ubuntu官网上的教程

  b.Windows安装FreeNX客户端,连接时打开所有能够提高性能的选项,并将连接所需的缓存设高一点

13.Ubuntu下的gnome-connection-manager, 相当于windows下的secureCRT。注意可以用ctrl + shift +c 和 ctrl +  shift + v 做复制和粘贴

14.Ubuntu下的geany, 很不错的文本编辑器

15.用synergy让windows与ubuntu共享mouse和keyboard

  a. windows端装一个gui,用作server

  b. ubuntu装QuickSynergy,用作客户端

  c. 配置:windows端要定义两个screen名字,分别用于两个系统;再定义几个link,如mouse从windows端右移时,会进入到ubuntu端。

16.MyEntunnel

17.Privoxy.
配置参照

18. IE Proxy Toggle

用VNC和Freenx连ubuntu的详细情况

配置:Server在ubuntu, Client在windows.

VNC

1.若Ubuntu用户已登录,从windows连接可以成功

2.若Ubuntu用户登录后锁屏,从windows连接可以成功

3.若Ubuntu用户登录后选择“休眠”、“挂起”或“注销”,从windows无法连接

4.Ubuntu用户登录后并且操作人从windows连接成功后,若Ubuntu切换用户,原有的windows连接将断开,试图重连也会失败;但可以使用另一个VNC地址(同一IP:1)来连,连好后看到的将是Ubuntu当前用户的界面

上述现象只所以发生,是因为
VNC只是把当前Ubuntu中当前登录用户能看到的东西共享给windows而已

FreeNX

只要Ubuntu开机了,从windows用FreeNX连Ubuntu就能成功,不管当时的Ubuntu有没有人登录,也不管当时有谁登录了; 从windows登录后,用户会感觉自己开机打开了Ubuntu,不会看到已打开的程序,即使相同的登录名已经登录了

这是
因为FreeNX完全基于SSH,每次登录都相当于开了一个新会话

不过,用FreeNX明显慢于VNC(如果你用的软件是UltraVNC的话)

适用场景:

   1.Ubuntu不休眠、注销,那用VNC就够了

   2.如果远程的Ubuntu被人休眠、注销、重启了,那就再用FreeNX连上去

谁加载了我 – 2.Tomcat6里面的web应用

public class MyServlet extends HttpServlet {
 

	protected void doGet(HttpServletRequest req, HttpServletResponse response)
			throws ServletException, IOException {
		 

		print(Servlet.class.getClassLoader());  //输出org.apache.catalina.loader.StandardClassLoader,它负责加载/common下的类库; Servlet.class就是common/servlet-api.jar里的类

		print(HttpServlet.class.getClassLoader()); //同上 
		//看看它的父加载器
		print(HttpServlet.class.getClassLoader().getParent()); //是sun.misc.Launcher$AppClassLoader,即system classloader

		
		print(MyServlet.class.getClassLoader());//输出org.apache.catalina.loader.WebappClassLoader,它负责加载当前web-app下的类库; MyServlet.class就是当前web-app下的类
		//看看它的父加载器
		print(MyServlet.class.getClassLoader().getParent()); //输出 org.apache.catalina.loader.StandardClassLoader
		
		 
		//试着找一下 Tomcat启动入口类的加载器 
		print(this.getClass().getClassLoader().loadClass("org.apache.catalina.startup.Bootstrap").getClassLoader());  //输出sun.misc.Launcher$AppClassLoader,即system classloader
		 

	//看看当前的context class loader是哪个		
        print(Thread.currentThread().getContextClassLoader()); //输出org.apache.catalina.loader.WebappClassLoader,即加载当前web-app的加载器	 
    }
}

谁加载了我 – 1.普通的Main程序

在普通的Main程序中,
某个类是被哪个加载器所加载的?

		print(String.class.getClassLoader()); //打印null, 这在HotSpot里代表Bootstrap ClassLoader,它负责加载$JAVA_HOME/lib里的类库
		print(List.class.getClassLoader()); // 同上

		
		
		
		print(learn.classloader.WhoLoadsMe.class.getClassLoader()); // 打印sun.misc.Launcher$AppClassLoader,它在HotSpot里代表System Class Loader, 负责加载用户类路径上的类库						
		print(learn.classloader.Friend.class.getClassLoader()); //同上
		
		//看看sun.misc.Launcher$AppClassLoader的父加载器和祖父加载器是哪些
		print(learn.classloader.Friend.class.getClassLoader().getParent()); //打印sun.misc.Launcher$ExtClassLoader,它在HotSpot里用于加载$JAVA_HOME/lib/ext下的类库
		print(learn.classloader.Friend.class.getClassLoader().getParent().getParent()); //打印null, 即Bootstrap ClassLoader
		
		
		
		
		Class<?> remoteFriendClass = new learn.classloader.RemoteClassLoader().loadClass(
				"learn.classloader.RemoteFriend");  
		print(remoteFriendClass.getClassLoader());  //打印 learn.classloader.RemoteClassLoader,即自写的class loader(后文有这个class loader的代码)

		//看看自写加载器的父加载器是哪个
		print(remoteFriendClass.getClassLoader().getParent()); //打印sun.misc.Launcher$AppClassLoader,即System ClassLoader

		print(remoteFriendClass.getField("friendOfFriend").getType()
				.getClassLoader()); //同上, 这意味着在"RemoteFriend"里直接引用的类(后文附上了代码描述这个引用)是由RemoteFriend的加载器所加载的


		//最后看下context class loader
		print(Thread.currentThread().getContextClassLoader()); //打印sun.misc.Launcher$AppClassLoader,即system classloader


附:
RemoteClassLoader

package learn.classloader;
public class RemoteClassLoader extends ClassLoader {
	@Override
	public Class<?> loadClass(String name) throws ClassNotFoundException {
		Class<?> clazz = findLoadedClass(name);
		if (clazz != null) {
			return clazz;
		}
		clazz = letParentLoad(name);
		if (clazz != null) {
			return clazz;
		}

		String classDir = "/home/kent/another-project/classes/";
		String classFile = classDir + name.replaceAll("\\.", "\\/") + ".class";
		byte[] bytes = readToBytes(classFile);
		return defineClass(name, bytes, 0, bytes.length);
	}
...
}

附:
RemoteFriend类里的FriendOfFriend

package learn.classloader;

public class RemoteFriend {
	
	public static FriendOfFriend friendOfFriend = new FriendOfFriend();

}

一步一步地理解Visitor模式

现在能找到的Visitor模式的讲解大都非常跳跃,问题列出来出后,讲着讲着就突然给出了生涩的UML类图和accept()、visit()等奇怪的方法名,让人非常费解; 读者即使理解了,也有囫囵吞枣的感觉,不能领会其精妙之处。

本文试图以问题为驱动,以代码重构的方式,展示我们是怎么样一步一步地解决问题,并选择visitor模式作为重构的终点。

#1.问题域

  visitor模式用于遍历一群对象,这些对象按某种结构组织在一起(List, Set, Tree等)。这种场景常常面临的问题是:

  
组织里的对象的类型彼此不同,遍历者要根据不同的类型使用不同的逻辑,导致代码里频繁使用if语句,可读性、可维护性都会比较差

    //这个方法逐个打印每个Employee的称谓
    private static void printTitle(Team team) {
        //一个Team里面的Employee有两种类型:Manager, 或Worker
        for (Employee employee : team.getEmployees()) {
            if (employee instanceof Manager) {
                System.out.println("Manager " + employee.getName());
            }
            if (employee instanceof Worker) {
                System.out.println("Worker " + employee.getName());
            }
        }
    }

   
代码详见
https://github.com/chenjianjx/learn-visitor-pattern/blob/master/1st-PlainSolution/src/learn/visitor/research/client/EmployeeClient.java

  要消除if,最常见的方式就是把各个if里面的逻辑塞入到对象的各个子类中

#2.通过多态解决对象类型不同的问题

    public class Manager extends Employee {
        @Override
        //让子类实现各自的printTitle()逻辑
        public void printTitle() {
            System.out.println("Manager " + this.getName());
        }
    }
  
    private static void printTitle(Team team) {
        for (Employee employee : team.getEmployees()) {
            employee.printTitle();
        }
    }
    //...

   
代码详见
https://github.com/chenjianjx/learn-visitor-pattern/blob/master/2nd-AddPolyform/src/learn/visitor/research/client/EmployeeClient.java

   这样就好多了。但又有新的问题:

    a. 对Team里所有Employee的遍历除了printTitle(),还有别的一些行为(如“喝饮料”);如果把这些互不相关的行为都塞到Employee类里,那么Employee及其子类就会“虚胖”,内聚性会比较弱

    b. 进一步说,这些行为都不应算做Employee的内在职责。如果Employee类要负责“喝饮料”的逻辑,那“吃东西”、“去银行”、“世界和平”岂不是也都要往里塞?

    所以我们要从被访问对象中剥离出这些方法,但我们又不能丢失前面的多态带来的好处。解决办法就是:
遍历时仍然调用被访问对象的多态方法,但这个方法:

    a.
使用暖味的方法名,如 doAction(),以表示不负责任何具体的行为

    b.
方法的实现委托给一个delegate,以表示自己不关心具体的实现

#3. 将被访问对象的服务契约暧昧化

    private static void drink(Team team) throws Exception {
        for (Employee employee : team.getEmployees()) {
            //使用暖味的doAction()方法名,接收行为的名称作为参数
            employee.doAction("drink");
        }
    }

    private static void printTitle(Team team) throws Exception {
        for (Employee employee : team.getEmployees()) {
            //使用暖味的doAction()方法名,接收行为的名称作为参数
            employee.doAction("printTitle");
        }
    }

  public class Manager extends Employee {
    
    @Override
    public void doAction(String action) throws Exception {
        Method method = ActionDelegate.class.getMethod(action + "Manager", Manager.class);
        method.invoke(actionDelegate, this);
    }
  }

   
代码详见
https://github.com/chenjianjx/learn-visitor-pattern/blob/master/3rd-SingleActionMethod/src/learn/visitor/research/client/EmployeeClient.java

    问题解决了吗? 只是部分解决了。访问者仍需要显式地传入行为的名称,如"printTitle"; 被访问对象还需要根据行为的名称找到处理它的delegate类或者delegate类中处理它的方法。

   

    从某种意义上说,if/else仍有残余:“若行为是printTitle,则调用delegate的printTitleXXX()方法".

    为了消除这种残余, 我们应该再次引入多态:

       a.
构建“行为”对象,如"Action"

       b.
把“行为名称”的区别变成子类类型的区别

       c.
把行为的逻辑作为多态的方法来实现

#4. 构建多态的“行为”对象

public class Manager extends Employee {

    @Override
    //被访问对象只接受虚的行为对象,并不知道传进来的具体是什么行为
    public void doAction(Action action) throws Exception {
        //被访问对象简单地回调行为对象,实现行为的逻辑
        action.handleManager(this);
    }
}
    private static void drink(Team team) throws Exception {
        DrinkAction action = new DrinkAction();
        for (Employee employee : team.getEmployees()) {
            //把虚的"行为“对象传给被访问对象
            employee.doAction(action);
        }       
    }

    private static void printTitle(Team team) throws Exception {
        PrintTitleAction action = new PrintTitleAction();
        for (Employee employee : team.getEmployees()) {
            employee.doAction(action);
        }
    }



   
代码详见
https://github.com/chenjianjx/learn-visitor-pattern/blob/master/4th-ActionPolyform/src/learn/visitor/research/client/EmployeeClient.java

问题解决了! 被访问对象不再受行为逻辑的任何污染,只须提供一个暖味的钩子(doAction())而已

说了这么多,那这跟visitor模式又有什么关系呢? 其实上面的解决方案就是visitor模式的解决方案。doAction() 就是visitor术语里的accept(),而handleManager就是visitor术语里的visitManager() 

#5. 使用visitor风格的方法命名,完事

    private static void drink(Team team) throws Exception {
        DrinkVisitor visitor = new DrinkVisitor();
        for (Employee employee : team.getEmployees()) {
            employee.accept(visitor);
        }      
    }

    private static void printTitle(Team team) throws Exception {
        PrintTitleVisitor visitor = new PrintTitleVisitor();
        for (Employee employee : team.getEmployees()) {
            employee.accept(visitor);
        }
    }

   
代码详见
https://github.com/chenjianjx/learn-visitor-pattern/blob/master/5th-VisitorStyle/src/learn/visitor/research/client/EmployeeClient.java

git入门

为什么要用git?

    1.
svn之类的"Centralized Version Control Systems"有什么问题?

     a.服务器的单点问题。服务器一旦当机,所有人都无法提交改动了。

     b.如果服务器硬盘坏了,而又没有备机,那你将损失所有revision history

    2.
git之类的"Distributed version control system" 如何解决上述问题?

     客户端存有的文件并不仅仅是最新版本,而是整个repository的镜像。每次checkout都是一次完整的复制。

      a.如果服务器硬盘坏了,可以根据客户端的文件恢复

      b.由于你镜像了整个repository,所以很多操作都只需在本地完成,速度很快; 而且如果服务器连不上,照样可以看revision history

    

基本概念

  1.
git的三种状态

    a.committed  — 修改已存入本地数据库

    b.modified   — 文件被修改,但还没有提交到本地库

    c.staged     — 文件被修改,并且已经被你标识了“将要”进库 ?

  2.
几种文件/目录的定义

    a. Git directory — 相当于svn的repository目录,用于存放元数据;既然git是"distributed", 所以你的本地和服务器上都会有这个目录。

    b. Working directory — 相当于svn的working directory

    c. Staging area — 是一个文件,记录哪些内容将被提交。这个文件一般在Git directory里面。

  3.
工作流

    a. 在working directory里修改文件,文件进入modified状态

    b. 标识这些文件,文件进入staged状态,Staging area被更新

    c. 提交。Staging area里的文件会进入working directory,并进入committed状态

例示git命令的使用

  1.
把某个已有的项目加入git

     cd myproject

     git init   #把本目录里变成git directory

     git add *.java  #加入所有文件

     git commit   #提交 

  2.
增改文件

    #增

    git add README

    git status

    git commit

    #改

    vi README

    git diff

    git status #将提示Changed but not updated,下次提交时不会提交任何东西   

    git add README #这就是stage,告诉git下次我将提交README

    git status

    git diff –staged

    git commit

   

    #不用git add,直接提交

    vi README

    git commit -a

  3.
玩远程的

    git remote add origin git@github.com:chenjianjx/learn-visitor-pattern.git  #定义一个remote repository,取名为origin

    git remote -v #察看刚刚定义好的remote repository

    git push -u origin master #将本地所有更新提交到远程repository

    #直接clone一个

    git clone git://gitorious.org/opentaps/opentaps-2.git opentaps-2

    cd opentaps-2

   

(待续…)

用java attach api + java instrument api写java agent的注意事项

用java attach api + java instrument api写java agent的注意事项

1. 字节码的transformation一旦发生,就会一直生效;agent的detach或中止,并不会使transformation回滚。

2. 由于#1,对于一个目标进程,在允许retransform的条件下,如果先后两次启动agent,就会导致两次transformation的效果叠加起来。举例来说,如果你的transform是在某方法里插入一条日志语句,那如果你先后两次使用你的agent,就会导致被transform的类在执行时生成两条日志。

待续…..

jip和btrace分别是怎么暴露进程数据的?

jip 和 btrace 是两个著名的开源工具,它们都可以察看另一个jvm进程的信息:jip的客户进程可以打印另一个进程中每个方法的执行时间,btrace则可以直接查看甚至改变变量的值。

两个工具可以做的事都直接依赖java instrument api。通过这个api在目标进程运行时修改字节码,在一定程度上改变代码的执行:jip让目标进程给出执行时间,btrace让目标进程给出变量的值。

jip和btrace在执行时都有自己的进程。它们自己的进程接收目标进程释放的信息,然后展现出来。
现在的问题是,工具自己的进程是怎么样跟目标进程通信的?

经研究发现:

  1.
jip进程与目标进程是通过原生的socket进行通信的。目标进程在启动时须使用-javaagent
jip-xxx.jar这个jvm参数(java instrument的“Command-Line Interface”)立即进行字节码instrument操作。具体的操作中包含一步:启动一个ServerSocket. jip进程启动后再访问这个ServerSocket.

  2.
btrace进程与目标进程则是通过java attach api进行通信的。通过指定一个进程号,btrace进程可以拿到目标进程的句柄。这个句柄是VirtualMachine类的实例,而这个类有一个方法叫loadAgent(),通过这个方法,btrace进程再把预先准备好的agent注入到目标进程实现字节码的动态修改(java instrument的"Starting Agents After VM Startup"模式)

优劣性比较

  假设目标进程是java web应用,两者的优劣点可以从多个方面来比较。

    1.
运行时的侵入性:btrace优于jip. jip需要在启动目标进程时就使用-javaagent参数,这可能导致贵公司服务器的启动脚本的改动。你们的运维人员未必愿意帮忙。

    2.
能否instrument远程的进程:jip优于btrace,因为btrace所基于的java attach api只能捆绑本机的进程,而jip基于的socket没有这个限制。

    3.
实现的简易性:btrace优于jip. 使用现成的attach api,当然比手工实现一套基于socket的c/s模型要省事。

    4.
平台无关性: jip胜出,因为btrace基于的attach api有两大制约:

       a. java6 以后才有这个api

       b. 这个api本身没有javax.*或java.*的接口;相反hotspot使用了com.sun.*作为包名,IBM jdk则使用了com.ibm.*作为包名,其它的jdk则未必实现了attach api规范。

PMBOK 学习笔记 3.7 项目风险管理

项目风险管理

  
风险源于不确定性

  风险管理的基本步骤:
识别风险 =>
分析风险 =>
规划应对方案

第0步:
制定风险管理计划,即
为整个风险管理制定方法论。包括:

      a.风险概率的定义:如规定“发生概率超过70%的风险为高可能性风险”

      b.风险影响的定义:如规定“二级风险指使进度拖延10%-20%的风险”

      c.风险管理活动将占用的时间和资金

第1步:
识别风险

      a.项目成员应该参与识别过程,这样可以提高大家的责任感

     b.进行识别时可以对照以前项目总结出来的风险checklist

      c.
识别出的风险应成文归档,放在“风险登记册”中

      d.项目进展过程中可能冒出新的风险,因此“识别风险”是一个持续的过程

第2步:
实施定性风险分析
分析出风险的发生概率和影响,并排定风险的优先级

第3步:
实施定量风险分析(没看懂,略过)   

第4步:
规划风险应对

    a.规避风险:如延长进度,缩小范围,使风险消失

    b.转移风险:把属于自己的风险转移到别人头上,如让甲方为超支的预算买单。

    c.减轻风险:降低风险发生的概率(比如测试做得好一点),或者减少它的影响(比如增加备机)

    d.接受风险:付出风险发生的代价,或者准备好B计划

第N/A步:
监控风险

     跟踪已识别风险,监测残余风险,识别新风险。这是一个持续进行的过程