stone2083

#

关于cookie特殊字符的一点理解

背景:
加密的cookie信息中带有特殊字符(“=”),导致读cookie的时候,特殊符号丢失,解密失败

看了同事“关于cookie特殊字符”的说明邮件,和网上对cookie特殊字符问题的解释:

我们在实际使用Cookie过程中要注意一些问题:

  1. Cookie的兼容性问题

  Cookie的格式有2个不同的版本,第一个版本,我们称为Cookie Version 0,是最初由Netscape公司制定的,也被几乎所有的浏览器支持。而较新的版本,Cookie Version 1,则是根据RFC 2109文档制定的。为了确保兼容性,JAVA规定,前面所提到的涉及Cookie的操作都是针对旧版本的Cookie进行的。而新版本的Cookie目前还不被Javax.servlet.http.Cookie包所支持。

  2. Cookie的内容

  同样的Cookie的内容的字符限制针对不同的Cookie版本也有不同。在Cookie Version 0中,某些特殊的字符,例如:空格,方括号,圆括号,等于号(=),逗号,双引号,斜杠,问号,@符号,冒号,分号都不能作为Cookie的内容。这也就是为什么我们在例子中设定Cookie的内容为“Test_Content”的原因。

  虽然在Cookie Version 1规定中放宽了限制,可以使用这些字符,但是考虑到新版本的Cookie规范目前仍然没有为所有的浏览器所支持,因而为保险起见,我们应该在Cookie的内容中尽量避免使用这些字符。

摘自:http://swingchen.bokee.com/6200015.html
类似这样的解释,搜索出来的结果,挺多。

但是,我去看了RFC2109(http://www.faqs.org/rfcs/rfc2109.html),其说明如下:

value中的token,是有一组非特殊字符,非空白字符。而它是在RFC 2068(http://www.faqs.org/rfcs/rfc2068.html)中制定的 (是对Header的规范),请看:


也就是说,所谓的Cookie1,同样有特殊字符的限制。
同样,在Cookie2(RFC2965)中,也如此。

想想也是啊,如果没有特殊字符的限制,解析Header的时候,还不乱套了?

看了RFC之后,我们再来看看Tomcat中的实现(6.0.29版本),请看:
org.apache.tomcat.util.http.Cookies

1.类注释:
A collection of cookies - reusable and tuned for server side performance.
Based on RFC2965 ( and 2109 )
是基于RFC2965/RFC2109规范来实现的

2.特殊字符的定义
/*
    List of Separator Characters (see isSeparator())
    Excluding the '/' char violates the RFC, but 
    it looks like a lot of people put '/'
    in unquoted values: '/': ; //47 
    '\t':9 ' ':32 '\"':34 '(':40 ')':41 ',':44 ':':58 ';':59 '<':60 
    '=':61 '>':62 '?':63 '@':64 '[':91 '\\':92 ']':93 '{':123 '}':125
    
*/
    
public static final char SEPARATORS[] = { '\t'' ''\"''('')'','
        
':'';''<''=''>''?''@''[''\\'']''{''}' };
根据规范,定义了特殊字符。除了“/”这个符号。因为大多数人会直接使用“/”。

3.针对“=”特殊处理
/**
 * If true, cookie values are allowed to contain an equals character without
 * being quoted.
 
*/
public static final boolean ALLOW_EQUALS_IN_VALUE;

static {
    ALLOW_EQUALS_IN_VALUE 
= Boolean.valueOf(System.getProperty(
            
"org.apache.tomcat.util.http.ServerCookie.ALLOW_EQUALS_IN_VALUE",
            
"false")).booleanValue();
}
可以在catalina.properties中,添加这个配置项 (或者启动过程中加上-D参数),使得cookie value中允许存在“=”符号。
所以本文开头提到的问题,可以使用这个方法得到解决

4.解析过程
/**
 * Parses a cookie header after the initial "Cookie:"
 * [WS][$]token[WS]=[WS](token|QV)[;|,]
 * RFC 2965
 * JVK
 
*/
public final void processCookieHeader(byte bytes[], int off, int len){
    
//详细代码,省略
}


备注:
RFC没有仔细看(时间有限,并且看E文挺累的),如理解有误,请告知。


posted @ 2010-11-03 13:27 stone2083 阅读(7159) | 评论 (1)编辑 收藏

爬取交通违章信息的脚本

     摘要: 会开车了,也慢慢不规矩起来了,于是乎,违章信息也慢慢多起来了,但是无法第一时间通知到自己。 虽说,有个网站:http://www.hzti.com/service/qry/violation_veh.aspx?pgid=&type=1&node=249 可以查询非现场违章情况, 不过: 1.我是懒人,主动去查询的时候不太乐意做 2.车辆识别码,永远记不住 3.每次输验证...  阅读全文

posted @ 2010-10-29 18:30 stone2083 阅读(3111) | 评论 (8)编辑 收藏

关于java多线程的几个小贴士

原文地址:
http://weblogs.java.net/blog/caroljmcdonald/archive/2009/09/17/some-java-concurrency-tips

大纲:

Prefer immutable objects/data
尽可能使用不变对象/数据

Threading risks for Web applications
注意web应用的线程安全问题

Hold Locks for as short a time as possible
持有锁的时间尽可能短

Prefer executors and tasks to threads
尽可能使用JDK并发工具包提供的Executor框架,进行多线程操作

Prefer Concurrency utilities to wait and notify
尽可能使用JDK并发工具包提供的工具进行同步(等待和通知)

  • Concurrent Collections
    • ConcurrentMap
    • ConcurrentHashMap
    • COncurrentLinkedQueue
    • CopyOnWriteArrayList
  • BlockingQueue Implementations
    • ArrayBlockingQueue
    • LinkedBlockingQueue
    • PriorityBlockingQueue

Producer Consumer Pattern
了解生产者消费者模式

Synchronizers
同步器

  • Semaphore
  • CountDownLatch
  • CyclicBarrier
  • Exchanger

Multithreaded Lazy Initialization is tricky
多线程环境下,lazy init是一件棘手的事情

Prefer Normal initialization
尽可能使用正常的初始化(尽可能不要使用lazy init)


posted @ 2010-09-15 17:37 stone2083 阅读(1905) | 评论 (2)编辑 收藏

java反射效率

     摘要: java反射效率到底如何,花了点时间,做了一个简单的测试.供大家参考. 测试背景: 1. 测试简单Bean(int,Integer,String)的set方法 2. loop 1亿次 3. 测试代码尽可能避免对象的创建,复发方法的调用,仅仅测试set方法的耗时 测试结果:  场景 &...  阅读全文

posted @ 2010-09-15 14:04 stone2083 阅读(8017) | 评论 (9)编辑 收藏

IBatis下DAO单元测试另类思路

在说另类思路之前,先说下传统的测试方法:
0.准备一个干净的测试数据库环境
  这个是前提
1.测试数据准备
  使用文本,excel,或者wiki等,准备测试sql以及测试数据
  利用dbfit,dbutil等工具将准备的测试数据导入到数据库中
2.执行dao方法
  执行被测试的dao方法
3.测试结果断言
  利用dbfit,dbutil等工具,断言测试结果数据和预计是否一致
4.所有数据回滚

其实,对于这个流程来说,目前的dao测试框架,支持的已经比较完美了
但是此类测试方法,也有明显的缺点(或者不能叫缺点,叫使用比较麻烦的地方)
如下:
1.背上了一个数据库环境.
  不轻量
  这是一个共享环境,谁也无法确保环境数据是否真正的干净
2.测试数据准备是一件麻烦的事情
  新表,10几个字段毫不为奇;老表,50几个字段甚至百来个字段,也偶有可见;无论是使用文本,excel,wiki,准备工作量,都是巨大的.
  准备的数据,部分字段内容可以是无意义的,部分字段内容又是需要符合测试意图(testcase设计目的),部分字段还是其他表的关联字段.从而导致后续维护人员无法了解准备数据意图.
  (实践中,也出现过,一同事在维护他人单元测试时,由于无法了解测试数据准备意图,宁可重新删除,自己准备一份)
3.预计结果数据准备也是一件麻烦的事情
  理由如上

所以,理论上是完美的测试方案,在实践过程中,却是一件麻烦的事情.导致DAO单元测试维护困难.


分析了现状,我们再来分析下,IBatis下DAO,程序员主要做了哪些编码:
1. 写了一份sqlmap.xml配置文件
2. 通过getSqlMapClientTemplate.doSomething($sqlID,$param), 执行语句
(当然,没有使用spring的同学,也是使用了类似sqlMapClient.doSomething($sqlID,$param)方法)

而步骤2其实是框架替我们做了的事情,按照MOCK的思想,其实这部分代码可以被MOCK的,那么我们是否可以做如下假设:
只要sqlmap.xml中配置信息(主要包括resultmap和statement)是正确的,那么执行结果也应该是正确的.

而我所谓的另类思路,就是基于这个假设,得出的:
IBatis下,DAO单元测试,我们抛弃背负的数据库环境,只要根据不同的条件,断言不同的sql即可.

于是乎,封装了一个IbatisSqlTester,可以根据sqlmap中的statement和传入的条件参数,生成sql语句.
那么,DAO单元测试就简单了,脱离下数据库环境:
public class ScoreDAOTest extends TestCase {
 
    @SpringBeanByName
    
private IbatisSqlTester ibatisSqlTester;  //通过spring配置,需要注入sqlmapclient对象
 
    @Test
    
public void testListTpScores() {
        Map
<String, Object> param = new HashMap<String, Object>(1);
        param.put(
"memberIds"new String[] { "stone""stone2083" });
        SqlStatement sql 
= ibatisSqlTester.test("MS-LIST-SCORES", param);
        
// sql全部匹配
        SqlAssert.isEqual("select * from score where member_id in ('stone','stone2083')", sql.toString());
        
// sql包含member_id,athena2002,stone关键词
        SqlAssert.keyWith(sql.toString(), "member_id""stone""stone2083");
        
// sql符合某个 正则
        SqlAssert.regexWith(".* where member_id in .*", sql.toString());
        
        
//其中,SqlAssert也可以换 成want.string()中的方法.
    }
}

优势:
  脱离了数据库环境
  脱离了表结构数据准备
  脱离了预计结果数据准备
  让单元测试变成sql的断言,编写相对更简单
缺点:
 
row mapper过程无法被测试


最后,附上两个核心的代码类(还未完成),供大家参考:
SqlStatement.java
/**
 * <pre>
 * SqlStatement:Sql语句对象.
 * 包含:
 *  1.sql语句,类似  select * from offer where id = ? and member_id = ?
 *  2.参数值,类似 [1,stone2083]
 *  
 *  toString方法,返回执行的sql语句,如:
 *  select * from offer where id = '1' and member_id = 'stone2083'
 * </pre>
 * 
 * 
@author Stone.J 2010-8-9 下午02:55:36
 
*/
public class SqlStatement {

    
//sql
    private String   sql;
    
//sql参数
    private Object[] param;

    
/**
     * <pre>
     * 输出最终执行的sql内容.
     * 将sql和param进行merge,产生最终执行的sql语句
     * </pre>
     
*/
    @Override
    
public String toString() {
        
return merge();
    }

    
/**
     * <pre>
     * 将sql进行格式化.
     * 
     * 目前只是简单进行格式化.去除前后空格,已经重复空格
     * TODO:请使用统一格式化标准规,建议使用SqlFormater类,进行处理
     * </pre>
     * 
     * 
@param sql
     * 
@return
     
*/
    
protected String format(String sql) {
        
if (sql == null) {
            
return null;
        }
        
return sql.toLowerCase().trim().replaceAll("\\s{1,}"" ");
    }

    
/**
     * <pre>
     * 将sql和param进行merge.
     * TODO:请严格按照SQL标准,进行merge sql内容
     * </pre>
     
*/
    
protected String merge() {
        
if (param == null || param.length == 0) {
            
return this.sql;
        }
        String ret 
= sql;
        
for (Object p : param) {
            ret 
= ret.replaceFirst("\\?""'" + p.toString() + "'");
        }
        
return ret;
    }

    
public String getSql() {
        
return sql;
    }

    
public void setSql(String sql) {
        
this.sql = format(sql);
    }

    
public Object[] getParam() {
        
return param;
    }

    
public void setParam(Object[] param) {
        
this.param = param;
    }
}

IbatisSqlTester.java
/**
 * <pre>
 * IBtatis SQL 测试
 * 一般IBatis DAO单元测试,主要就是在测试ibatis的配置文件.
 * IbatisSqlTester将根据提供的Sql Map Id 和 对应的参数,返回 {
@link SqlStatement}对象,提供最终执行的sql语句
 * 通过外部SqlAssert对象,将预计Sql和实际产生的Sql进行对比,判断是否正确
 * </pre>
 * 
 * 
@author Stone.J 2010-8-9 下午02:58:46
 
*/
public class IbatisSqlTester {

    
// sqlMapClient
    private ExtendedSqlMapClient sqlMapClient;

    
/**
     * 根据提供的SqlMap ID,得到 {
@link SqlStatement}对象
     * 
     * 
@param sqlId: sql map id
     * 
@return @see {@link SqlStatement}
     
*/
    
public SqlStatement test(String sqlId) {
        
//得到MappedStatement对象
        MappedStatement ms = sqlMapClient.getMappedStatement(sqlId);
        
if (ms == null) {
            
//TODO:建议封转自己的异常对象
            throw new RuntimeException("can't find MappedStatement.");
        }

        
//按照Ibatis代码,得到Sql和Param信息
        RequestScope request = new RequestScope();
        ms.initRequest(request);
        Sql sql 
= ms.getSql();
        String sqlValue 
= sql.getSql(request, null);

        
//组转返回对象
        SqlStatement ret = new SqlStatement();
        ret.setSql(sqlValue);
        
return ret;
    }

    
/**
     * 根据提供的SqlMap ID和对应的param信息,得到 {
@link SqlStatement}对象
     * 
     * 
@param sqlId: sql map id
     * 
@param param: 参数内容
     * 
@return @see {@link SqlStatement}
     
*/
    
public SqlStatement test(String sqlId, Object param) {
        
//得到MappedStatement对象
        MappedStatement ms = sqlMapClient.getMappedStatement(sqlId);
        
if (ms == null) {
            
//TODO:建议封转自己的异常对象
            throw new RuntimeException("can't find MappedStatement.");
        }

        
//按照Ibatis代码,得到Sql和Param信息
        RequestScope request = new RequestScope();
        ms.initRequest(request);
        Sql sql 
= ms.getSql();
        String sqlValue 
= sql.getSql(request, param);
        Object[] sqlParam 
= sql.getParameterMap(request, param).getParameterObjectValues(request, param);

        
//组转返回对象
        SqlStatement ret = new SqlStatement();
        ret.setSql(sqlValue);
        ret.setParam(sqlParam);
        
return ret;
    }

    
/**
     * 设置SqlMapClient对象
     
*/
    
public void setSqlMapClient(ExtendedSqlMapClient sqlMapClient) {
        
this.sqlMapClient = sqlMapClient;
    }

    
/**
     * <pre>
     * 不推荐使用
     * 推荐使用: {
@link IbatisSqlTester#setSqlMapClient(ExtendedSqlMapClient)}
     * TODO:请去除这个方法,或者增加初始化的方式
     * </pre>
     * 
     * 
@param sqlMapConfig sqlMapConfig xml文件
     
*/
    
public void setSqlMapConfig(String sqlMapConfig) {
        InputStream in 
= null;
        
try {
            File file 
= ResourceUtils.getFile(sqlMapConfig);
            in 
= new FileInputStream(file);
            
this.sqlMapClient = (ExtendedSqlMapClient) SqlMapClientBuilder.buildSqlMapClient(in);
        } 
catch (Exception e) {
            
throw new RuntimeException("sqlMapConfig init error.", e);
        } 
finally {
            
if (in != null) {
                
try {
                    in.close();
                } 
catch (IOException e) {
                }
            }
        }
    }

}


最后的最后附上所有代码(通过单元测试代码,可以看如何使用).欢迎大家的讨论.
sqltester
builder

posted @ 2010-08-12 09:03 stone2083 阅读(3524) | 评论 (9)编辑 收藏

扫描classpath下类资源

很早之前,为了简化配置信息,自己写了一坨代码,基于classpath扫描类信息,加载.
其实,在spring中,已经提供了类似组件(后知后觉了...):

org.springframework.core.io.support.PathMatchingResourcePatternResolver  资源解析器(基于路径的正则表达式)
org.springframework.core.type.classreading.MetadataReader ClassMeta信息解读器

于是乎,代码就非常简单了:
 1 public class Test {
 2 
 3     /* 资源路径 */
 4     private static final String                  PATH           = "classpath*:com/alibaba/javalab/t*/**/*.class";
 5     /* 资源解析器 */
 6     private static final ResourcePatternResolver RESOLVER       = new PathMatchingResourcePatternResolver();
 7     /* Meta信息Reader Factory.用于创建MetaReader */
 8     private static final MetadataReaderFactory   READER_FACTORY = new SimpleMetadataReaderFactory();
 9 
10     public static void main(String[] args) throws Exception {
11         //根据正则表达式,得到资源列表
12         Resource[] resources = RESOLVER.getResources(PATH);
13         for (Resource res : resources) {
14             //通过 MetadataReader得到ClassMeta信息,打印类名
15             MetadataReader meta = READER_FACTORY.getMetadataReader(res);
16             System.out.println(meta.getClassMetadata().getClassName());
17         }
18     }
19 }

输出结果:
com.alibaba.javalab.tool.fetion.protocol.Config
com.alibaba.javalab.tool.fetion.protocol.Fetion
com.alibaba.javalab.tool.fetion.protocol.FetionHelper
com.alibaba.javalab.tool.fetion.protocol.LoginSession
com.alibaba.javalab.tool.trace.TimeTrace
...

挺好使的一个工具 :)


posted @ 2010-07-23 14:55 stone2083 阅读(1182) | 评论 (0)编辑 收藏

初识InheritableThreadLocal

一直来只知道ThreadLocal,直到最近看slf4j MDC实现代码的时候,才认识了InheritableThreadLocal.
InheritableThreadLocal顾名思义,可继承的ThreadLocal.
看类描述:
This class extends <tt>ThreadLocal</tt> to provide inheritance of values
 * from parent thread to child thread: when a child thread is created, the
 * child receives initial values for all inheritable thread-local variables
 * for which the parent has values.

测试代码:
 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         //使用ThreadLocal,父子线程之间,不共享Value
 5         final ThreadLocal<String> tl = new ThreadLocal<String>();
 6         tl.set("ThreadLocal-VAL");
 7         System.out.println("Main-1:" + tl.get());
 8         new Thread() {
 9             public void run() {
10                 System.out.println("Child-1:" + tl.get());
11             };
12         }.start();
13 
14         //使用InheritableThreadLocal,父线程Value可让子线程共享
15         final ThreadLocal<String> itl = new InheritableThreadLocal<String>();
16         itl.set("InheritableThreadLocal-VAL");
17         System.out.println("Main-2:" + itl.get());
18         new Thread() {
19             public void run() {
20                 System.out.println("Child-2:" + itl.get());
21             };
22         }.start();
23 
24     }
25 }

输出内容:
Main-1:ThreadLocal-VAL
Main-2:InheritableThreadLocal-VAL
Child-1:null
Child-2:InheritableThreadLocal-VAL


......分隔符号......

顺带着简单说下MDC.(Mapped Diagnostic Context). 中文直译太恶心了,我理解的意思是,和环境相关的上下文信息.
比如在web应用中,我们可以把用户的ip,访问url等放入到这个上下文中,log打印的时候,就能得到这个信息.

在slf4j BasicMDCAdapter实现中,就是用了InheritableThreadLocal
1 public class BasicMDCAdapter implements MDCAdapter {
2 
3   private InheritableThreadLocal inheritableThreadLocal = new InheritableThreadLocal();
4   
5   //.
6 
7 }

posted @ 2010-07-23 09:17 stone2083 阅读(2577) | 评论 (0)编辑 收藏

小谈PropertyPlaceholderConfigurer

背景:让应用在一个环境下,以多实例的方法运行.
Log问题,可以通过Log4j占位符实现(见前文:http://www.blogjava.net/stone2083/archive/2010/07/01/324935.html)
其他Java组件代码依赖了本地环境资源,怎么解决呢?

对于使用Spring的组件来说,PropertyPlaceholderConfigurer能帮我们解决这一问题.

PropertyPlaceholderConfigurer除了支持配置的properties文件外,还支持系统属性(System.getProperties()).当然,它有三种模式:
 1/** Never check system properties. */
 2public static final int SYSTEM_PROPERTIES_MODE_NEVER = 0;
 3 
 4/**
 5 * Check system properties if not resolvable in the specified properties.
 6 * This is the default.
 7 */
 8public static final int SYSTEM_PROPERTIES_MODE_FALLBACK = 1;
 9 
10/**
11 * Check system properties first, before trying the specified properties.
12 * This allows system properties to override any other property source.
13 */
14public static final int SYSTEM_PROPERTIES_MODE_OVERRIDE = 2;

对于使用本地环境资源的bean来说,只要配置:
 1 <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
 2     <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
 3     <property name="locations">
 4         <list>
 5             <value>classpath*:spring/env.properties</value> <!--无需配置node-->
 6         </list>
 7     </property>
 8 </bean>
 9 
10 <bean id="javaBean" class="com.alibaba.javalab.spring.JavaBean">
11      <property name="lockFile" value="/home/stone/base/${node}/lock" />
12 </bean>


在启动脚本中,只要加入-Dnode=instanceX即可.

总结:
PropertyPlaceholderConfigurer支持properties文件和系统属性.并且存在三种覆盖策略.

posted @ 2010-07-23 08:51 stone2083 阅读(1273) | 评论 (0)编辑 收藏

新人破冰问题(挺搞笑,挺恶毒)

转自:http://blog.renren.com/blog/226112318/452978694


  1. 初次访问发生在几点几分?
  2. 完全打开首页花费多少时间?
  3. 是否浏览完整个首页后再去找login入口?
  4. 找login入口花了多少时间?
  5. 是否在服务器提示下找到入口?
  6. 在找到真正login页面之前,是否误入后台login页面?
  7. 是否使用XX助手找到入口?
  8. 输错了几次密码后成功登陆?
  9. 在第一次成功登陆的时候,是否使用https(安全连接)?
  10. 登陆之前购买了多少份https证书?
  11. 是否由于服务器带宽太小导致登入很慢?
  12. 登陆成功后服务器是否发出提示音?
  13. 登陆之后产生了多少PV才最终下单?
  14. 服务器是否在初次下单后给出红色回执?
  15. 下单之后留在处于登陆状态几分钟才离开?
  16. 整个访问过程一共产生了几个session?
  17. 平均session时长是几分钟?
  18. 当日访问的cookie类型是cookie2.2还是3.1?
  19. 当日一共下了多少单?
  20. 在之后的30日内的活跃度类型(5次-18次属于中度活跃度)


实践经验:
1. 对于封闭式题目(回答是与否),比较没劲:问问题的人描述了半天,回答者只回答一个是否者否.对于此类问题,要做改进
2. 有些问题,都不好意思问出口
3. 千万要根据新人的性格,决定是否是否这套模板,切忌切忌

posted @ 2010-07-23 08:32 stone2083 阅读(8663) | 评论 (0)编辑 收藏

Java Exception性能问题

     摘要: 背景: 大学里学java,老师口口声声,言之凿凿,告诫我们,Java千万别用异常控制业务流程,只有系统级别的问题,才能使用异常; (当时,我们都不懂为什么不能用异常,只知道老师这么说,我们就这么做,考试才不会错 :) ) 公司里,有两派.异常拥护者说,使用业务异常,代码逻辑更清晰,更OOP;反之者说,使用异常,性能非常糟糕; (当然,我是拥护者) 论坛上,争论得更多,仁者见仁智者见智,口...  阅读全文

posted @ 2010-07-09 14:30 stone2083 阅读(13725) | 评论 (16)编辑 收藏

仅列出标题
共10页: 上一页 1 2 3 4 5 6 7 8 9 下一页 Last