2009年6月23日星期二
Oracle EM Console 更改hostname后无法启动
emca -config dbcontrol db
2009年5月14日星期四
Oracle三种常用Join
Indexed Nested Loops
The Nested Loop join is an iterative join: for each row in the first (inner) row source, lookup matching rows in the second (outer) row source. If the nested lookup of the second row source performs a Unique or Range Index Scan, then we call this Indexed Nested Loops.
Indexed Nested Loops is used primarily in low volume joins; it is efficient over small volumes and versatile enough to be used in a variety of situations. Although it is fully scalable, Indexed Nested Loops is inefficient over large data volumes.
Hash Join
The hash join is used for high-volume equi-joins (joins with equals predicates). Oracle performs a single read of the smaller row source (call this T1) and builds a hash table in memory. The join key is used as the hash-key of the hash table. Then a single pass of the larger row source (call this T2) is performed, hashing the join key of each row to obtain an address in the hash table where it will find matching T1 rows.
Provided T1 remains small enough to build the hash table in memory, T2 can be scaled up to any arbitrarily large volume without affecting throughput or exceeding temp space. If T1 cannot be hashed in memory, then a portion of the hash-table spills to disk. When the hash table is probed by T2, the rows with join keys that match those parts of the in-memory hash table are joined immediately; the rest are written to TEMP and joined in a second pass. The bigger T1 is, the smaller the proportion of the hash table that can fit in memory, and the larger the proportion of T2 that must be scanned twice. This slows the Hash Join down considerably and also makes the join non-scalable.
Sort-Merge
A sort-merge join works by reading each row-source in the join separately; sorting both sets of results on the join column(s); then concurrently working through the two lists, joining the rows with matching keys. Sort-Merge is generally faster than Indexed Nested Loops but slower than Hash Join for equi-joins. It is used almost exclusively for non-equi joins (>, <, BETWEEN) and will occasionally be used when one of the row sources is pre-sorted (eg. a GROUP BY inline view)
If both row sources are small then they may both be sorted in memory, however large sorts will spill to disk making then non-scalable.
There is no way to make a Sort-Merge join scalable. The only other way to resolve a non-equijoin is to use Nested Loops, which is slower. As volumes increase, Sort-Merge will continue to out-perform Nested Loops, but will eventually run out of Temp space. The only solution is to extend TEMP, or convert the join to Nested Loops (and then wait).
2009年3月23日星期一
Don't cache Singleton object in a serializable object
如果你有一个对象其中某个字段保存了一个对Singleton的引用,那么这个对象在序列化读取后会导致在同一个虚拟机里Singleton对象有两个。比如下面的例子
public class Test3 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
System.out.println(ABC.getInstance());
ByteArrayOutputStream out = new ByteArrayOutputStream();
new ObjectOutputStream(out).writeObject(ABC.getInstance());
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray()));
ABC abc = (ABC) in.readObject();
System.out.println(abc);
}
}
class ABC implements Serializable {
private ABC() {}
private static ABC instance = new ABC();
public static ABC getInstance() {
return instance;
}
}
你会发现,打印出的两个ABC的instance是不一样的。解决方法很简单,在ABC加入这个方法
protected Object readResolve() {
return instance;
}
这样可以重置instance到这个虚拟机中的Singleton实例,还可以把引用instance的字段作为transient字段节省IO时间。
但是这不是最好的办法,最好就是,如果你知道ABC是Singleton,那么就永远不要把ABC赋值到你的对象成员变量里,getInstance()因为是static方法,编译器通常会做inline的,频繁调用不会导致频繁方法栈操作,所以缓存它意义不大。
2009年3月22日星期日
Literal Pitfall
public static final int APRIL = 3
public static final int MAY = 4
public static final int JUNE = 5
...
我相信很多人也是这样定义常量或者枚举型。其实这样会有一个很严重的问题,和编译器的行为有关系。VM Spec 2.17.4中描述类初始化的发生条件时提到ClassA的某个常量字段比如ClassA.MAX被访问的时候不会导致ClassA类被初始化。
2.17.4 Initialization
........
A class or interface type T will be initialized immediately before one of the following occurs:
* T is a class and an instance of T is created.
* T is a class and a static method of T is invoked.
* A nonconstant static field of T is used or assigned. A constant field is one that is (explicitly or implicitly) both final and static, and that is initialized with the value of a compile-time constant expression. A reference to such a field must be resolved at compile time to a copy of the compile-time constant value, so uses of such a field never cause initialization.
原因是如果ClassB引用ClassA.MAX,编译器会把ClassA.MAX的常量值复制到ClassB的常量池中。这样显然效率更高。
我们做一个实验,有两个类
public class ConstClass {
public static final int TEST = 5;
}
public class RefClass {
public static void main(String[] args) {
System.out.println(ConstClass.TEST);
}
}
编译后,执行RefClass明显应该打印5,我们用javap看一下pesudo code:
public class RefClass extends java.lang.Object{
public RefClass();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: iconst_5
4: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
7: return
}
这里你会看到iconst_5,RefClass并没有让VM加载ConstClass,事实上,你删除ConstClass.class也没有关系。
问题来了,如果你这样定义常量或者枚举值,将来如果ClassA.MAX的值你需要更改,那么你必须重新编译所有引用过这个值的类!那些类需要重新编译,这是非常难预测的,尤其是被频繁使用的API.
如何克服呢?有两种方式,一种是提供一个getTEST()来返回常量值,比如把刚才的类改成
public class ConstClass {
public static final int TEST = 5;
public static int getTEST() {
return TEST;
}
}
public class RefClass {
public static void main(String[] args) {
System.out.println(ConstClass.getTEST());
}
}
由于getTEST()是static,编译器可能会inline他,因此效率不会太低
还有一种方式,是jdk1.5之后提供的enum
让我们重新写这两个类
public enum ConstClass2 {
TEST(5);
private int value;
ConstClass2(int v) {
this.value = v;
}
public int getValue() {
return this.value;
}
}
public class RefClass2 {
public static void main(String[] args) {
System.out.println(ConstClass2.TEST.getValue());
}
}
在用javap看看引用类
public class RefClass2 extends java.lang.Object{
public RefClass2();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: getstatic #3; //Field ConstClass2.TEST:LConstClass2;
6: invokevirtual #4; //Method ConstClass2.getValue:()I
9: invokevirtual #5; //Method java/io/PrintStream.println:(I)V
12: return
}
你会发现这次5没有被复制到引用类的常量池,相反getstatic代替了iconst_,相当于ClassA.MAX会被解释成ClassA.getMAX(),这样效果其实和上面说的另外一种方法getTEST()是类似的。
其实enum还有其他的好处,JDK guide中说 (http://java.sun.com/j2se/1.5.0/docs/guide/language/enums.html)
- Not typesafe - Since a season is just an
int
you can pass in any other int value where a season is required, or add two seasons together (which makes no sense). - No namespace - You must prefix constants of an int enum with a string (in this case
SEASON_
) to avoid collisions with other int enum types. - Brittleness - Because int enums are compile-time
constants, they are compiled into clients that use them. If a new
constant is added between two existing constants or the order is
changed, clients must be recompiled. If they are not, they will still
run, but their behavior will be undefined. - Printed values are uninformative - Because they are
just ints, if you print one out all you get is a number, which tells
you nothing about what it represents, or even what type it is.
其中第三点就是本文描述的问题,另外typesafe也是个问题,比如你完全可以把Integer.MAX_VALUE 传给 Calendar.set(Integer.MAX_VALUE, somevalue),另外没有命名空间而且打印出来也很不友好。
所以,总之,还是enum吧?
2009年1月18日星期日
用 PhantomReference 避免OutOfMemory
PhantomReference比Weak/Soft的引用强度都要低,PhantomReference.get()总是返回null,为什么呢?
其实PhantomReference而要是不可缺少的重要引用类型, 我们知道WeakReference在finalize方法调用或被gc清理之前会进入ReferenceQueue, 这时候没有任何strong reference引用对象,然后可以通过ReferenceQueue去做收尾工作。但是在finalize()方法,或者重新给这个对象一个引用使它reachable(从某个活动线程调用栈,或者静态变量可达), 使这个对象延长生命周期暂时不会被gc清理。而PhantomReference只会在对象被从内存中清除后才会进入队列,get()总是返回null (ReferenceQueue通知一个PhantomReference的时候,既然内存都已经物理释放了,当然也无法给你一个对象,所以get总是返回null也有这个原因), 所以你没有办法重新使这个对象再次reachable。PhantomReference只是提供了一种方式让你跟踪一个曾经产生过的对象,由此让你知道这个对象到底有没有被物理的清除。
比如当你的applet程序需要处理一个非常大的图片的时候,你可能希望图片处理结束并且内存被释放之后再处理下一个图片。如果用PhantomReference来引用上一个图片对象,当ReferenceQueue通知你的时候,你就可以知道上一个内存对象已经被物理清除,你可以继续下一个大内存对象的处理了。这样就可以避免由于GC线程优先级低导致上一个大内存对象还没有释放下一个大内存对象又被创建,让OutOfMemoryError出现的概率低一些。还有一个好处,就是PhantomReference比用finalize方法好的多,因为VM对finalize的处理不如PhantomReference简单可靠,只不过是你要写的代码稍微多一点点而已。
2009年1月4日星期日
Oracle 的锁基础原理
Oracle有以下类别的锁,本文主要只讨论DML的锁
•DML locks (data locks)
•DDL locks (dictionary locks)
•Oracle Internal Locks/Latches
•Oracle Distributed Locks
•Oracle Parallell Cache Management Locks
DML的锁是针对并发数据访问的,因此分行级别TX和表级别TM
两种经典锁
共享锁S表示其他事务可以读取这个资源但是任何事务都不可以更改这个资源,任何事物都不可以再加X锁。
排他锁X表示其他事务可以读取资源,但只有当前事务可以更改这个资源,其他任何事物都不可以再加S或者X锁。
行级锁
也叫事务锁,简称TX锁。两种经典锁,行级别上面只支持X锁(排它锁),Oracle没有行级别S锁(共享锁)。所有DML操作一般自动生成行级锁,行级别锁理论上比表级别锁效率高,因为他的锁定范围更小更精确。
Oracle当中一个记录被insert/update/delete/select for update之后,当前事务T1自动获得这行的X排他锁,另外一个事务T2如果要更改这条记录,另外一个事务T2会被阻塞,直到T1提交或者回滚。
比如
步骤 | T1 | T2 | 备注 |
1 | update row1 | ||
2 | update row1 | T2被阻塞 | |
3 | commit | T1提交,T2可以继续 | |
4 | commit |
如果在第一步之后,你执行select * from v$lock where type in ('TX','TM'),你就会看到有一个行级别锁TX
其中LMODE=3和REQUEST=0,其中LMODE是已经获得的锁模式,取值从0到6, 对于行级别锁其实唯一的值是6,其他值都是对表级别锁的。REQUEST表示要求的锁,如果一个事务所要求的锁需要等待另一个事务释放,那么REQUEST可以看到这个事务需要请求什么类型的锁。
0 | NO LOCK | |
1 | NULL | |
2 | RS (Row Share) | |
3 | RX (Row Exclusive) | |
4 | S (Share) | |
5 | SRX (Share + Row Exclusive) | |
6 | X (Exclusive) |
这里这个行级别锁TX已经获得了在行row1上的排他锁。
如果在第二步执行之后,T2会被阻塞,你打开一个新的连接再一次执行select * from v$lock where type in ('TX','TM'),你就会看到多出来一个行级别锁TX
这个新多出来的行级别锁在row1上获得锁是0(无),要求锁是6(X),同时,刚才那个行级别锁的block标志位变成了1,表示有别的事务等待我释放这个锁。
然后如果你提交T1,T2获得锁,可以继续下去,相应的只剩下了T2的row1上的锁,而且LMODE从0变成了6, REQUEST从6变成了0
另外,这时候T1会自动在表级别也加一个锁,目的是防止事务过程中发生DDL操作。其中SID是session ID,可以通过V$session查看,ID1和ID2是rollback segment和transaction table entry.
表级锁
简称TM锁,可分为以下5种
RS: row share,意向锁,表示表内部分行有S锁了,部分行不许更改了
RX: row exclusive ,意向锁,表示表内部分行有变更,有X锁了
S: share ,整个表有S锁,这个表不可以更改
SRX: share + row exclusive,只有一个事务可以获得SRX锁,其他事务可以查询但是不可以更改
X: exclusive, 只有一个事务可以获得X表锁, 其他事务只能查询
RS,RX比较特殊,他们是DML操作产生的常见表级锁,他们都只是意向锁,真正的加锁粒度还是在行级,相当于某些行被锁之后,用这两个意向锁锁一下所属的表,数据库会比较容易判断行级别的锁情况。
从刚才的例子中可以看到,TX总是伴随着一个LMODE=3的TM锁,即RX锁,只要表中有行有X锁,表就会有RX锁。RX表示事务已经更改了某些行,比如insert/update/delete,获得了某些行X锁。RX不会阻塞RX锁,除非行锁上有阻塞。刚才的例子,如果两个事务更改的是两个记录,那么就不会互相阻塞。
如果执行了lock table test in row share mode你会看到LMODE=2(Row Share)的一个表级别锁。RS级别的锁只阻止X级别表锁,它不阻止RS, S等。
SQL Server, Oracle 和 MySQL 事务隔离等级实现差别
基本概念
脏读:包含未提交数据的读。例如,事务1 更改了某行。事务2 在事务1 提交更改之前读取已更改的行。如果事务1 回滚更改,则事务2 便读取了逻辑上从未存在过的行。
不可重复读取:当某个事务不止一次读取同一行,并且一个单独的事务在两次(或多次)读取之间修改该行时。因为在同一个事务内的多次读取之间修改了该行,所以每次读取都生成不同值,从而引发不一致问题。
幻像:通过一个任务,在以前由另一个尚未提交其事务的任务读取的行的范围中插入新行或删除现有行。带有未提交事务的任务由于该范围中行数的更改而无法重复其原始读取。如果某个连接设置其事务隔离级别为可串行,则 SQL Server 使用键范围锁定以防止幻像。
SQL-92 定义了下列四种隔离级别
隔离级别 | 脏读 | 不可重复读取 | 幻像 |
未提交读 | 是 | 是 | 是 |
提交读 | 否 | 是 | 是 |
可重复读 | 否 | 否 | 是 |
可串行读 | 否 | 否 | 否 |
我们使用三个主流数据库测试,分别是Oracle 10g, SQL Server 2005和MySQL 5.1 (InnoDB only)
测试幻读
首先创建一个表test,并插入三条测试数据
CREATE TABLE test
(
c1 INTEGER,
c2 INTEGER
);
insert into test values(1,1);
insert into test values(2,2);
insert into test values(3,3);
commit;
SQL Server消除幻读测试
Steps | T1 | T2 | 备注 |
1 |
|
| T1事务启动,设置隔离等级到Serializable, 然后查询一下test,会看到3条记录 |
2 |
select * from test; | T2启动,插入一行并提交,这时候SQL Server会阻塞这个插入, 因为T1加了范围锁 | |
3 | select * from test; | T1第二次查询应该看到和第一次一样的三行,然后提交 | |
| 只有T1提交之后,T2才可以插入 | ||
从sp_lock输出来看,在第一步之后,T1对test表加了S锁,所以T2可以选test的数据但是无法更改
Oracle消除幻读测试
Steps | T1 | T2 | 备注 |
1 |
|
| T1事务启动,设置隔离等级到Serializable, 然后查询一下test,会看到3条记录 |
2 |
select * from test; | T2启动,插入一行并提交,这时候Oracle不会阻塞这个插入,然后T2成功提交 | |
3 | select * from test; | T1第二次查询仍然看到和第一次一样的三行,然后提交 | |
4 | select * from test; | 新事物开始,这时候T1才会看到刚才T2插入的记录 | |
从V$lock来看,整个过程Oracle没有加锁,Oracle使用了SCN从回滚段重建出来当初时刻的数据提供一个snapshot 来保证T1不受T2的影响。
MySQL消除幻读测试
Steps | T1 | T2 | 备注 |
1 |
|
| T1事务启动,设置隔离等级到Serializable, 然后查询一下test,会看到3条记录 |
2 |
select * from test; | T2启动,插入一行并提交,这时候MySQL会阻塞这个插入, 因为T1加了范围锁 | |
3 | select * from test; | T1第二次查询应该看到和第一次一样的三行,然后提交 | |
| 只有T1提交之后,T2才可以插入 | ||
和SQL Server一样的处理方式,但是MySQL也有snapshot, 效果和Oracle一样,语法是START TRANSACTION WITH CONSISTENT SNAPSHOT;从这个角度来说MySQL实现还是不错的,比SQL Server好一些。
测试不可重复读
SQL Server消除不可重复测试
Steps | T1 | T2 | 备注 |
1 |
|
| T1事务启动,设置隔离等级到Serializable, 然后查询一下test,会看到3条记录 |
2 |
select * from test; | T2启动,更改一行,这时候SQL Server会阻塞这个更改, 因为T1在记录行上加了X和U锁 (排他更新锁) | |
3 | select * from test; | T1第二次查询应该看到和第一次一样记录,然后提交 | |
| 只有T1提交之后,T2才可以更新 | ||
Oracle消除不可重复测试
Oracle不支持REPEATABLE READ这个隔离等级,我们需要用更高的Serializable级别来测试
Steps | T1 | T2 | 备注 |
1 |
|
| T1事务启动,设置隔离等级到Serializable, 然后查询一下test,会看到3条记录 |
2 |
select * from test; | T2启动,更改一行并提交,这时候Oracle不会阻塞这个更新,然后T2成功提交 | |
3 | select * from test; | T1第二次查询仍然看到和第一次一样的记录,然后提交 | |
4 | select * from test; | 新事物开始,这时候T1才会看到刚才T2更改的记录 | |
从V$lock来看,整个过程Oracle没有加锁,Oracle使用了SCN从回滚段重建出来当初时刻的数据提供一个snapshot 来保证T1不受T2的影响。
MySQL消除不可重复测试
Steps | T1 | T2 | 备注 |
1 |
|
| T1事务启动,设置隔离等级到Serializable, 然后查询一下test,会看到3条记录 |
2 |
select * from test; | T2启动,更改一行并提交,这时候MySQL不会阻塞这个更新. | |
3 | select * from test; | T1第二次查询应该看到和第一次一样的记录 | |
对于不可重复读,MySQL和Oracle一样采取了类似快照的方式,有点出乎意料,这点上MySQL实现比SQL Server要好一些。
测试脏读
Read Committed可以消除脏读,由于比较简单,各个数据库实现差别不大,不会加特殊的锁,不再详细测试。这也是我们大多数情况下的数据库默认事务隔离等级。
总结
事务隔离等级不同的数据库实现有差别,有时候必须要清楚的了解之间的差别才能避免应用程序在多个数据库上兼容性和稳定性。
对于REPEATABLE READS,SQL Server设定隔离等级到REPEATABLE READ,对所有select过的数据加锁,阻止其他事务更新数据。但是其他事务可以插入幻读记录。而Oracle不直接支持这个等级,必须设定更高的SERIALIZABLE,Oracle使用SCN和Rollback segment重构snapshot,MySQL也是使用类似的snapshot,因次理论上讲SQL Server的这个隔离级别上并发性能比较差。
对于PHANTOM幻读,三种数据库都是设定到SERIALIZABLE等级,但是SQL Server是通过加范围锁阻塞其他事务的插入和更新,Oracle使用snapshot, MySQL两种方式都支持。
另外,由于SERIALIZABLE级别上,SQL Server使用的是范围锁,所以其他数据无法插入或者更新,而Oracle不会阻塞其他事务更新数据,Oracle假设大多数情况下多个事务不会更新同一条记录,但是如果其他更新的记录和当前事务碰巧修改了同一条记录,Oracel会通过乐观锁发现这种情况,并报错臭名昭著的ORA-8177 Cannot serialize access for this transaction. 所以Oracle的Serializable虽然性能高,但是不可以用于长时间的事务或者频繁的OLTP系统。如果有这样的需要,必须通过lock table实现。
尽管这几个数据库都实现了ACID但是各有千秋,实现跨数据库的应用的时候需要小心。还有就是,不管数据库本身事务隔离等级和锁的实现效率差别如何,关键还是良好的架构,不好的架构往往在单节点低压力测试的时候速度很快但是在高并发的多处理器或者集群上横向伸缩的时候性能下降很快,Facebook或者LiveJournal用一大堆MySQL照样跑的很好, 不比昂贵的Oracle RAC差,架构决定伸缩性,而不应该迷信数据库本身。
2008年12月27日星期六
Hibernate 的乐观锁@Version和 MySQL 的两个小问题
MySQL 6 才会支持毫秒,所以目前你只能用INT或者BIGINT来保存Version了。
但是就算是你用了INT你可能仍然遇到别的错误
Caused by: java.lang.NullPointerException
at org.hibernate.type.IntegerType.next(IntegerType.java:59)
at org.hibernate.engine.Versioning.increment(Versioning.java:108)
at org.hibernate.event.def.DefaultFlushEntityEventListener.getNextVersion(DefaultFlushEntityEventListener.java:365)
at org.hibernate.event.def.DefaultFlushEntityEventListener.scheduleUpdate(DefaultFlushEntityEventListener.java:257)
at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:128)
这个错误一看就头大了吧,Hibernate的源代码是这样的
public Object next(Object current, SessionImplementor session) {
return new Integer( ( (Integer) current ).intValue() + 1 );
}
初步分析原因是前面事件通知的时候传入的当前version是空的,具体看这里 http://opensource.atlassian.com/projects/hibernate/browse/HHH-3030
最简单的解决办法是给@version字段在数据库里设置一个默认值,比如0
Opensource意味着你不能发现bug的时候打电话对售后大喊大叫,没办法,要么自己写一个hotfix,要么等官方fix吧。
2008年11月27日星期四
Swing 的 Validate, Invalidate, Revalidate
Class Hierarchy
Component.invalidate():
Invalidates this component. This component and all parents above it are marked as needing to be laid out.
Component.validate()
Ensures that this component has a valid layout. This method is primarily intended to operate on instances of Container
Container.validate()
Validates this container and all of its subcomponents.
The validate method is used to cause a container to lay out its subcomponents again. It should be invoked when this container's subcomponents are modified (added to or removed from the container, or layout-related information changed) after the container has been displayed.
Set valid=true and call Container.validateTree().
Container.validateTree() will call all its sub containers’ validateTree() recursively, so all sub components’ Component.validate() method will be called.
Container.validateTree()
Container.invalidate()
Basically do the same thing as super class Component.invalidate except for notifying the layout manager the changes, since the major difference between Component and Container is that Container can contains objects with layout manager.
LayoutManager2.invalidate() will discard the cached size information about the layout, so next time the layout will be re-calculated.
Conclusion on validate/invalidate
Invalidate() causes the component hierarchy to be marked as needing to be laid out again, and the validate() causes that to be done. It may be expensive, but is a way of getting the peers to recalculate size and to do what is needed to bring the display up to date. It has limitations: it doesn't cause an immediate screen update when invoked from an event handler, where paintImmediately() does.
JComponent.revalidate()
Supports deferred automatic layout.
Calls invalidate and then adds this component's validateRoot to a list of components that need to be validated. Validation will occur after all currently pending events have been dispatched. In other words after this method is called, the first validateRoot (if any) found when walking up the containment hierarchy of this component will be validated. By default, JRootPane, JScrollPane, and JTextField return true from isValidateRoot.
Conclusion on revalidate
This method will automatically be called on this component when a property value changes such that size, location, or internal layout of this component has been affected. This automatic updating differs from the AWT because programs generally no longer need to invoke validate to get the contents of the GUI to update. Because RepaintManger.addInvalidComponent() will validate them.
2008年11月24日星期一
OpenSSL转换PEM(Base64 DER)格式到PFX(PKCS12)格式
C:\>openssl pkcs12 -export -in ca.cer -inkey ca.pem -out ca.pfx
Loading 'screen' into random state - done
Enter pass phrase for ca.pem:
Enter Export Password:
Verifying - Enter Export Password:
签名试一下
C:\>signtool sign /f ca.pfx /p password test.js
Done Adding Additional Store
Successfully signed: test.js
导入到JKS格式试一下
C:\Temp>keytool -importkeystore -srckeystore ca.pfx -srcstoretype PKCS12 -destkeystore ca.jks
Enter destination keystore password:
Re-enter new password:
Enter source keystore password:
Entry for alias 1 successfully imported.
Import command completed: 1 entries successfully imported, 0 entries failed or cancelled
2008年11月13日星期四
添加JarFinder.com按钮到google bar
你可以把JarFinder作为一个google bar的search按钮。只要随便添加一个google按钮,然后选择编辑,use the advanced editor (关于google bar 的教程请访问 http://toolbar.google.com/buttons/apis)
<?xml version="1.0" encoding="UTF-8"?>
<custombuttons xmlns="http://toolbar.google.com/custombuttons/">
<button>
<search method="get">http://www.jarfinder.com/index.php/java/search/~{query}~</search>
<site>http://www.jarfinder.com</site>
<title>JarFinder</title>
<icon type="image/x-icon" mode="base64">
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
aUcAAAAASUVORK5CYII=
</icon>
<description>JarFinder</description>
</button>
</custombuttons>
2008年11月11日星期二
免费杀毒软件AVG
找了一下免费的防病毒软件,综合对比了一下,觉得AVG的Basic版本比较适合我,免费而且轻量。
安装的时候我没有选择Link Scanner, E-Mail Scanner, Plugin for office, 因为我从来不轻易打开链接或者附件的,我需要的仅仅是文件系统实时保护。
装上之后感受了一下,资源管理器果然没有了延迟问题!估计查杀病毒的能力和功能肯定是不如SEP,但是对于我来说,够了。
2008年10月27日星期一
2008年10月14日星期二
SOAP中 RPC/encoded, RPC/literal, document/literal 之间区别
RPC是面向调用的,所以要求在payload中包含operation名字
而document方式是不包含operation名字的,payload里直接就是part
encoded包含part的类型信息,比如xsi:type="xsd:int"
literal是不包含part的类型信息但是通过引用schema里的元素也可以容易的通过schema验证
RPC/encoded
WSDL:
<message name="myMethodRequest">
<part name="x" type="xsd:int"/>
<part name="y" type="xsd:float"/>
</message>
<message name="empty"/>
<portType name="PT">
<operation name="myMethod">
<input message="myMethodRequest"/>
<output message="empty"/>
</operation>
</portType>
Payload on wire:
<myMethod>
<x xsi:type="xsd:int">5</x>
<y xsi:type="xsd:float">5.0</y>
</myMethod>
优点:
服务端通过payload的顶层元素operation-name就可以分发请求到底层实现类,不需要根据myMethod内的几个part的类型来找到到底调用的哪一个operation
缺点:
xsi:type="xsd:int"这种太长,也没必要,性能下降
验证麻烦,x, y是单独通过schema type验证,而myMethod不属于schema,属于WSDL定义。
不兼容WS-I,所以互操作性有问题
RPC/Literal
WSDL: 和RPC/encoded一样
Payload on wire:
<myMethod>
<x>5</x>
<y>5.0</y>
</myMethod>
优点:
服务端通过payload的顶层元素operation-name就可以分发请求到底层实现类,和RPC/encoded一样
type encoding 没有了,性能能上升
RPC/literal is WS-I compliant.
缺点:
不容易验证message因为不包含x和y的类型信息,myMethod也不是schema定义的。
Document/encoded
Nobody follows this style. It is not WS-I compliant. So let's move on.
Document/Literal
WSDL:多了包含schema的types元素
<types>
<schema>
<element name="xElement" type="xsd:int"/>
<element name="yElement" type="xsd:float"/>
</schema>
</types>
<message name="myMethodRequest">
<part name="x" element="xElement"/>
<part name="y" element="yElement"/>
</message>
<message name="empty"/>
<portType name="PT">
<operation name="myMethod">
<input message="myMethodRequest"/>
<output message="empty"/>
</operation>
</portType>
Payload on wire:
<xElement>5</xElement>
<yElement>5.0</yElement>
优点:
没有type信息,消息简短,传输性能好
所有payload内的内容都可以通过schema校验(xElement是引用types里的schema)
WS-I部分兼容(只有一个元素的时候才兼容)
缺点:
WSDL复杂,包含了太多types
operation name没有了,分发比较复杂(后面解释soapAction)
WS-I要求soap:body内只有一个元素,而这里有了两个,所以有时候不兼容WS-I
其中在HTTP头里加入soapAction可以解决分发问题,SOAP 1.1规范里说:The SOAPAction HTTP request header field can be used to indicate the intent of the SOAP HTTP request. The value is a URI identifying the intent
那么intent是什么呢?你需要在WSDL定义operation的时候给operation指定soapAction,这相当于一个operation的key或者id
当document/literal的时候,如果有两个operation用了同样的参数(类似于方法重载), 那么服务端无法区分是调用了哪个operation
这种情况下,必须要包含soapActiont头来指明到底哪个operation被调用。当然soapAction不要太长,否则还不如包含type的效率高
注意:在SOAP 1.2协议中,soapActionb变成了action。
Document/literal wrapped pattern
从这看出来,就算是document/literal虽然可以校验message但是没有了operation,RPC/literal相反,有operation但是校验message比较麻烦
如果两点都能满足的话多好?恩,现在事实上从微软开始,很多人逐渐采用了document/literal wrapped pattern来解决这个问题.
WSDL:
<types>
<schema>
<element name="myMethodRequest">
<complexType>
<sequence>
<element name="x" type="xsd:int"/>
<element name="y" type="xsd:float"/>
</sequence>
</complexType>
</element>
<element name="myMethodResponse">
<complexType/>
</element>
</schema>
</types>
<message name="myMethodRequest">
<part name="parameters" element="myMethodRequest"/>
</message>
<message name="myMethodResponse">
<part name="parameters" element="myMethodResponse"/>
</message>
<portType name="PT">
<operation name="myMethod">
<input message="myMethodRequest"/>
<output message="myMethodResponse"/>
</operation>
</portType>
Payload on wire
<myMethod>
<x>5</x>
<y>5.0</y>
</myMethod>
这么乍一看,payload和RPC/literal一样的,没有什么变化,其实不然。在RPC/literal中myMethod就是operation。但是在docment/literal wrapped中,myMethod是wrapper的名字,这个可以通过payload中唯一的input message得到operation,这样operation也可以得到了。
对于document/literal wrapped方式,有几个特点:
对于每一个operation, input message只有一个
part不是primitive类型,是一个complex element
wrapper和operation名字一样
wrapper没有任何atrribute
优点:
没有type encoding,消息短小,效率高
payload所有元素都可以有对应的schema校验(从根元素myMethod开始)
soap body里的wrapper就是operation,所以容易分发
wrapper增加的约束使得document/literal方式的payload只有一个元素(myMethod),满足了WS-I
缺点:
WSDL太长
本身只是一个style,不是标准或者规范
2008年10月10日星期五
一点点关于Restful Web Service的设计的思考
* 为所有“事物”定义ID
* 将所有事物链接在一起
* 使用标准方法
* 资源多重表述
* 无状态通信
如果你要实现这样的Restful WS那么你可能会遇到和我遇到的一样的一些问题:
问题1:面向资源和面向消息动作的服务
Restful Web Service是面向资源的服务,不同于SOAP是面向消息和动作的服务,Rest WS应该用URI来表示资源.
这就有一个问题,对于资源来讲是没有业务语义的,比如一个缺陷跟踪软件如果需要暴露这样两个服务:
1) update一个bug,对于SOAP来讲,在WSDL里暴露一个update的operation,服务端代码更新数据库就好了
2) reopen一个bug,对于SOAP来讲,你需要WSDL暴露一个operation是reopen,然后服务端代码除了会操作数据库更改bug的状态之外,还会发送email通知owner,并启动一个处理bug的工作流。
这两个操作本质上都会update bug的字段,但是由于两个操作有业务语义,所以他们是不同的,后者会启动一个业务工作流,而前者不会。
对于Restful Web Service来讲,更改bug在数据库的状态很简单,但是如何区分语义呢?我们知道这个HTTP PUT(或POST)操作在Restful WS中本身只知道我要更新资源,它本身是没有业务语义的,因为Rest WS是基于资源的服务,没有任何业务逻辑。
这个问题确实比较头疼,在现实世界中,大多数复杂的应用是粗颗粒基于消息和动作的,比如RPC.如果你需要对资源操作,那么客户端就要负责业务逻辑和事务性等,这对客户端是一个很大的麻烦。比如一个SOAP里deletePersons( names[])对应到RestWS就要客户端循环发送DELETE /persons/name这个请求,对于更复杂的运算客户端不得不更多的了解服务端的内部数据关系,严重破坏了封装性。另外,这种情况下客户端也很难实现事务性。
但是反过来说,现实世界中暴露的服务,可能80%的操作还是最基本的CRUD(增读改删),这个比较适合Rest,清晰简单,可能20%的操作还是有很强的业务语义的操作,更适合SOAP,这个比例每个项目会不太一样,但是肯定是CRUD比较多。那么如果你要使用Restful WS如何平衡呢?我的想法是,做一些违反Restful规则的服务。比如deletePersons(names[])这个操作,你可以发一个请求 POST /persons?deletePersons,然后请求体包含一段XML或者JSON包含所有的name.事实上很多互联网网站提供的Restful API也是这种面向资源和动作的服务的混合体,正是那句话,没有银弹。
问题2,客户端说,该死,Restful不是规范所以更不会有WSDL,我怎么知道怎么调用?
另外Restful WS没有WSDL那样的规范,即便你用JSON或者ATOM协议,具体内容(比如atom的content tag)仍然是没有具体规定的,你可以纯文本或者POX(Plain Old XML)或者base64的内容放到atom的content tag里,没有限制,如果资源是一个树状结构的复杂数据实体,那么客户端怎么才能知道如何产生请求报文和解析响应呢?
我的想法:提供MDS(meta data service),暴露一个全局的服务,告诉客户端每个服务支持哪些格式,比如如果你的内容是POX那么这个MDS应该会是很多schema,如果内容也可以是JSON格式,因为JSON没有schema,但是可以用BNF范式,不过BNF读起来实在不是很方便,你可以用JSON-XML转换的规则来套用XML的Schema (http://www.ibm.com/developerworks/library/x-atom2json.html, 我更喜欢这个 http://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html) 。还有一种方式,就是把复杂的资源拆分为多个小的资源,保证所有资源背后的数据都是flat的key/value pair,这样你就可以通过POST/PUT做表单提交一样的更新数据。但是这个工作有些时候对调用者不太方便,事务性也很难保证。
Restful WS没有任何WS-Enumeration, WS-Policy, WS-Security, WS-Transaction这样的协议集合,所以你需要自己来实现,这是没有标准的。这么看来Restful WS确实比较单薄,但是或许正是因为Restful 比较简单所以才会使用越来越广泛。这也不是一个大问题,你可以尽量利用现有技术或者实现自己的方案,比如安全性你可以使用证书和HTTPS的保证验证和传输安全。
问题3,我该用PUT 还是 POST?
根据HTTP规范,PUT适用于做安全的幂等性操作(idempotent),换句话说,两次操作是安全的,不会导致不同的结果。
比如一条SQL:update gender='male' where name='daniel'执行两次不会造成不同的结果
类似的还有:createOrUpdate(user1)调用两次也没有问题
非幂等性的操作就不能保证这一点,比如,一条SQL:update counter=counter+1 where name='x'执行两次就会产生不同的结果,再比如create(user1)调用两次会创建两个一样的用户,这也会产生不同的结果。
这意味着PUT其实可以看作是createOrUpdate操作,比如
PUT /persons/daniel第一次会创建daniel这个人,第二次请求会更新daniel这个人,如果两个请求内容一样,那么更新操作其实不会对第一次创建的daniel做任何改动。
和PUT一样GET,HEAD,PUT,DELETE,OPTIONS和TRACE都有这种性质.
DELETE可以用于删除,GET可以用于读取。
比较特殊的是POST,他蕴含的意思是,嘿,客户端,调用我的话,我可不保证幂等性,我可能会启动一个工作流,也可能会插入一个消息到另一个系统,我什么都能做,但我就是不能保证你调用我两次同样的请求会出现什么后果。
上面我们提到过的Restful WS是没有面向消息和动作的服务的,如果我们要提供的话,其实POST是最好的选择,他可以做任何事情。而PUT更适合做面向资源的服务,用来创建或者更新一个数据。
参考HTTP规范http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html:
The fundamental difference between the POST and PUT requests is reflected in the different meaning of the Request-URI. The URI in a POST request identifies the resource that will handle the enclosed entity. That resource might be a data-accepting process, a gateway to some other protocol, or a separate entity that accepts annotations. In contrast, the URI in a PUT request identifies the entity enclosed with the request -- the user agent knows what URI is intended and the server MUST NOT attempt to apply the request to some other resource.
但是Atom Publishing Protocol这种经常被用作Restful WS的协议去不是这样的,她就是用PUT做更新,POST做创建。为什么呢?我觉得现实世界中,大多数情况下当你创建一个entry你是不知道他的主键的,这个主键90%的情况下都是服务器端生成的自增主键。如果你用PUT去创建一个entry,比如PUT /rest/person/daniel, 这隐含着daniel就是person的主键值,这里不能有两个人都叫daniel。而事实上当你创建一个新的person你通常是不指定主键值,而是服务器端返回一个社会福利号码或者身份证编号这样的主键,所以如果你还用PUT那么请求看起来应该是这样PUT /rest/person, 然后创建出来的person的URI /rest/person/12434320456。乍一看没问题,其实问题很严重,PUT URI之后,资源的URI不应该变化,这里你的URI从没有后面的数字ID变成了有数字ID, 这种对一个URL提交然后在服务端处理后在另外一个URL暴露你提交的数据,这不是POST在HTTP规范里定义的行为么?所以,呵呵,APP认为大多数情况下ID是服务端生成的,所以用POST更合适。
那到底我们应该用什么呢?PUT还是POST创建记录?
我觉得,如果你知道新纪录的主键,比如客户端可以生成GUID你就可以用PUT,90%的情况下都是自增主键,那你还是用POST。