OGNL-training (30 minutes) Xiang yingbing 2011-05-15 All Rights Reserved © Alcatel-Lucent 2007, ##### Content List 1. Express Language (EL)在J2EE(MVC)中的应用 2. OGNL 2.1 History 2.2 Syntax 2.3 Expressions 3. OGNL与ANTLR /JavaCC(Java Compiler Compiler ) 3.1 OGNL源代码是怎么生成出来的 3.2 AMS和ANTLR/JSMIPARSER 4. OGNL examples 4.1 ArrayCreationTest 4.2 ognl\src\test\java\org\ognl\test 4.3 ArithmeticAndLogicalOperatorsTest.java 2 All Rights Reserved © Alcatel-Lucent 2007 1. Express Language (EL)在 J2EE(MVC)中的应用 (a)model 3 All Rights Reserved © Alcatel-Lucent 2007 (b)controller 4 All Rights Reserved © Alcatel-Lucent 2007 (c)view 5 All Rights Reserved © Alcatel-Lucent 2007 说明: view中,#{menuMBean.getMenu(topicQueryMBean.mid).getName()} 就是个 EL表达式。 Controller中,@ManagedBean(name=“menuMBean”)中“menuMBean”是个简 单的EL表达式。 OGNL最开始作为MVC架构中UI组件和Controllers之间的一种关联方法,利用属性名字进行关联。 JSF/JSP页面如何显示给用户(web browser) Web browser发送request(list.xhtml)到web container Web container 读取list.xhtml文件,把所有的#{…}用适当的内容替换掉。 这个替换的过程就是对EL表达式的解析过程。 替换完毕后, Web container 把得到的html页面发送回web browser。 不同的web container/WEB FRAMEWORK采用不同的EL 实现。 Struts采用OGNL。 JSF: sun EL(jsf-impl.jar\com\sun\faces\el ) JBossEL: http://anonsvn.jboss.org/repos/jboss-el 6 All Rights Reserved © Alcatel-Lucent 2007 7 All Rights Reserved © Alcatel-Lucent 2007 2. OGNL OGNL是Object-Graph Navigation Language的意思;它是一个表达式语言,用于获取 Java对象的属性,或者为之设置属性。使用相同的表达式来读取属性或者设置属性。 主页 OGNL 旧的主页: http://www.ognl.org/ [现在已经不可访问] OGNL 新的主页: http://www.opensymphony.com/ognl/ OGNL由OpenSymphony开发,该公司其它产品 • WebWork • Quartz • OSCache • OSWorkflow • SiteMesh • Compass • OGNL • Tonic • XWork • OSUser • PropertySet • Clickstream • OSCore 8 All Rights Reserved © Alcatel-Lucent 2007 文档 Language Guide (PDF) 下载 cvs sourcecode: http://svn.opensymphony.com/svn/ognl/ 官方maven下载:http://maven.opensymphony.com/opensymphony/jars/ ognl-2.6.10-javadoc.jar 15-Feb-2007 10:53 321K ognl-2.6.10-sources.jar 15-Feb-2007 10:53 198K ognl-2.6.10.jar 15-Feb-2007 10:53 164K ognl-2.6.11-javadoc.jar 18-Feb-2007 07:47 321K ognl-2.6.11-sources.jar 18-Feb-2007 07:47 198K ognl-2.6.11.jar 18-Feb-2007 07:47 164K 应用( http://en.wikipedia.org/wiki/OGNL) • • • • • • • • • 9 WebWork and its successor Struts2 Tapestry Spring Web Flow Apache Click NReco (.NET integration framework for lightweight MDD) op4j (op4j-ognl extension) - Java fluent interface implementation. MyBatis - SQL mapper framework The Thymeleaf Template Engine - A Java XML/XHTML/HTML5 template engine Unitils - Modular Testing framework for Java All Rights Reserved © Alcatel-Lucent 2007 2.1 History OGNL最开始作为MVC架构中UI组件和Controllers之间的一种关联方法,利用属性名 字进行关联。随着对更复杂关联的需求的日益增加,Drew Davidson创建了一个他称为 KVCL的东西,用于Key-Value Coding Language,这是Luke Blanshard鼓动的。 Luke然后用ANTLR重新实现了该语言,取了个新名字,这次由Drew鼓动的,这就是 现在这种状态。后来Luke又用 JavaCC重新实现了该语言。所有代码的进一步维护都 由Drew完成 (Luke进行spiritual指导)。 10 All Rights Reserved © Alcatel-Lucent 2007 2.2 Syntax 简单的OGNL表达式非常简单。该语言带有各种features,变得非常丰富,但是 ,通常情况下,你不需要关心该语言的复杂部分:the simple cases have remained that way。比如说,要访问一个对象的name属性,OGNL表达式就 是name。要得到通过headline属性返回的对象的text属性,OGNL表达式 就是 headline.text。 OGNL表达式的基本单元是导航链(navigation chain),通常就叫做链“chain” 。最简单的chains包含下面几部分: 11 All Rights Reserved © Alcatel-Lucent 2007 举例:name.toCharArray()[0].numericValue.toString() This expression follows these steps to evaluate: • extracts the name property of the initial, or root, object (which the user provides to OGNL through the OGNL context) • calls the toCharArray() method on the resulting String • extracts the first character (the one at index 0) from the resulting array • gets the numericValue property from that character (the character is represented as a Character object, and the Character class has a method called getNumericValue()). • calls toString() on the resulting Integer object. The final result of this expression is the String returned by the last toString() call. 12 All Rights Reserved © Alcatel-Lucent 2007 2.3 Expressions EL表达式遵守的格式(语法) 下面先列举一些简单的例子,以及一些重要的例子。 完整内容请参见Language Guide (PDF)的第四章 简单的例子: array.length: 获取数组的长度 array[0]: 读取数组的第一个元素 array.length = array["length"] = array["len" + "gth"] session.attribute[“foo”] :从map中获取某key对应的值 13 All Rights Reserved © Alcatel-Lucent 2007 2.3.1 Variable References: #this OGNL在表达式的每个点号(.)那里存储该点号(.)之前的EL表达式的值,该值通 过#this来获取。 比如: listeners.size().(#this > 100? 2*#this : 20+#this) 第二个点(.)那里,存储的值为"listeners.size()",接着,在括号(...)内部,用 #this来指向这个值,也就是"listeners.size()"。整个表达式的结果: 如果listeners.size()大于100,那么为2*(listeners.size()); 否则,整个EL表达式的值为20+(listeners.size()) 14 All Rights Reserved © Alcatel-Lucent 2007 2.3.2 Chained Subexpressions 如果OGNL表达式中,某个点号(.)后面跟了个括号表达式(...),比如: headline.parent.(ensureLoaded(), name) 那么,这个点号(.)之前的所有表达式(这里为headline.parent)的值被传入括号表达式 内,作为该括号表达式的root object。上例(ensureLoaded(), name)等价于: (a) rootObj1=headline.parent (b) 调用rootObj1.ensureLoaded()方法 【方法调用,可以用来做一些动作!比如赋值,检测,日志...】 (c) 然后把rootObj1.name作为整个表达式的值。 还可以按照这种模式,做很长的链接: obj1.obj2.(methodOfObj2(), propertyOfObj2).p1.p2.obj3.(methodOfObj3() propertyOfObj3) 另外一个例子: obj1.(obj2).(obj3).(obj4).(obj5) 等价于:obj1.obj2.obj3.obj4.obj5 15 All Rights Reserved © Alcatel-Lucent 2007 其余内容,自己研究。 16 All Rights Reserved © Alcatel-Lucent 2007 17 All Rights Reserved © Alcatel-Lucent 2007 3. OGNL与ANTLR /JavaCC (Java Compiler Compiler ) 前面History中,我们提到: Luke然后用ANTLR重新实现了该语言,取了个新名字,这次由Drew鼓动的,这就是现在 这种状态。后来Luke又用 JavaCC重新实现了该语言。 JavaCC/ANTLR Java Compiler Compiler [tm] (JavaCC [tm]) is the most popular parser generator for use with Java [tm] applications. A parser generator is a tool that reads a grammar specification and converts it to a Java program that can recognize matches to the grammar. In addition to the parser generator itself, JavaCC provides other standard capabilities related to parser generation such as tree building (via a tool called JJTree included with JavaCC), actions, debugging, etc. JavaCC是一种grammar parser generator:语法解析器的生成器。也就是用于生成语法解析器。 ANTLR也是类似的工具 很多语言,比如Java,HTML,C/C++,OGNL,MIB,都遵循一定的格式。那么,就可 以用ANTLR/JAVACC为这些语言生成parser以解析这些语言。 18 All Rights Reserved © Alcatel-Lucent 2007 3.1 OGNL源代码是怎么生成出来的 ognl-2.6.9-sources\ognl\src\java\ognl • ArrayElementsAccessor.java • ArrayPropertyAccessor.java • ASTAdd.java • … • ognl.jjt • ognl.jj ognl-2.6.9-sources\ognl\build.xml 19 All Rights Reserved © Alcatel-Lucent 2007 用JAVACC生成OGNL的Compiler(OGNL源代码)过程: • <java classname=“org.javacc.jjtree.Main”…> 根据ognl.jjt文件生成一些java文 件和ognl.jj文件 • <java classname=“org.javacc.parser.Main” …>根据ognl.jj再生成一些java文件 。 • 生成的Java文件可以用来读取并解析符合OGNL语言规范的EL表达式。也就是说,这些 JAVA文件组成OGNL compiler。比如a+b,OGNL解析它的时候,就能理解该表达 式的意思是a加上b。 • 如果想要计算EL表达式的值,或者根据EL表达式为java对象设置值,那么需要手工加入 (OGNL开发人员已经完成)一些代码,一边OGNL解析完EL表达式的时候,接着执行 get/set。 20 All Rights Reserved © Alcatel-Lucent 2007 3.2 AMS和ANTLR/JSMIPARSER 【扩展内容,快速提示】 AMS使用了jsmiparser解析MIB,而jsmiparser采用了antlr语法解析器生成器。通OGNL一样 , jsmiparser也可以用JAVACC实现。 JSMIPARSER读取MIB文件 9.0.10\basic.fttbmdu.1.2\axs-fttbmdu-mobject-plugin (a) \src\main\resources\META-INF\mobject-mibs.xml 下面这个bean指示哪些mib文件名字(路径)被读入bean id="mibFileNames" 21 All Rights Reserved © Alcatel-Lucent 2007 (b)mobject-beans.xml 用SmiDefaultParser.parse解析mib文件,得到的结果: bean id="mib" 22 All Rights Reserved © Alcatel-Lucent 2007 23 All Rights Reserved © Alcatel-Lucent 2007 4. OGNL examples 先把ognl-2.6.9-sources\ognl导入eclipse,然后运行ognl-2.6.9sources\ognl\src\test\java\org\ognl\test中的test cases。 先看看改造的: 24 All Rights Reserved © Alcatel-Lucent 2007 4.1 ArrayCreationTest import ognl.ExpressionSyntaxException; … public class ArrayCreationTest { private static Root ROOT = new Root(); private static Object[][] TESTS = { // Array creation { ROOT, "new String[] { \"one\", \"two\" }", new String[] { "one", "two" } }, { ROOT, "new String[] { 1, 2 }", new String[] { "1", "2" } }, //… }; 25 All Rights Reserved © Alcatel-Lucent 2007 接上… public static void main(String[] args) throws Exception { String expressionString = ""; Object root; for(int i=0;i<TESTS.length;i++){ try{ root = TESTS[i][0]; expressionString = (String)TESTS[i][1]; SimpleNode expression = (SimpleNode)Ognl.parseExpression(expressionString); OgnlContext context = new OgnlContext(); //OgnlContext context = (OgnlContext)Ognl.createDefaultContext(null); Object expressionvalue = Ognl.getValue(expression, context, root);//表达式值 Object expectedvalue = TESTS[i][2];//期望值 System.out.println(expressionString+" test-->" + OgnlTestCase.isEqual(expressionvalue, expectedvalue)); }catch(Exception e){ if(e.getClass() == ExpressionSyntaxException.class){ System.out.println(expressionString+" test-->This is the expected Exception"); }else{ e.printStackTrace(); } } } } } 26 All Rights Reserved © Alcatel-Lucent 2007 4.2 ognl\src\test\java\org\ognl\test 所有测试classes的定义结构相同,都扩展org.ognl.test.OgnlTestCase 最终都是验证:表达式是否和某个期望值相同。运行过程: (a) //Root root= new Root(); Root root= null;// (b) OgnlContext context = (OgnlContext)Ognl.createDefaultContext(null); (c) SimpleNode expression = (SimpleNode)Ognl.parseExpression(expressionString); (d) assertTrue(isEqual(Ognl.getValue(expression , context, root), expectedResult)); 27 All Rights Reserved © Alcatel-Lucent 2007 4.3 ArithmeticAndLogicalOperatorsTest.java public class ArithmeticAndLogicalOperatorsTest extends OgnlTestCase { private static Object[][] TESTS = { // Double-valued arithmetic expressions { "-1d", new Double(-1) }, { "+1d", new Double(1) }, { "--1f", new Float(1) }, { "2*2.0", new Double(4) }, { "5/2.", new Double(2.5) }, { "5+2D", new Double(7) }, { "5f-2F", new Float(3) }, { "5.+2*3", new Double(11) }, { "(5.+2)*3", new Double(21) }, ...28 }; // BigDecimal-valued arithmetic expressions { "-1b", new BigDecimal(-1) }, { "+1b", new BigDecimal(1) }, { "--1b", new BigDecimal(1) }, ... ... { "#y + \"1\"", "11" }, { "\"1\" + #y", "11" } All Rights Reserved © Alcatel-Lucent 2007 这里 (a) root为空, (b) context为 context = (OgnlContext)Ognl.createDefaultContext(null); context.put("x", "1"); context.put("y", new BigDecimal(1)); (c)表达式TESTS [i][0]的值,总是等于期望值TESTS [i][1] 可以到源代码中看其它例子,设计web container/ams server的时候,这些例子非常有用。 29 All Rights Reserved © Alcatel-Lucent 2007 The END 30 All Rights Reserved © Alcatel-Lucent 2007