Go2FullStack
Go2FullStack
ValueStack, the data transfer mechanism behind Struts2

Ognl, ValueStack Basic

ValueStack is the mechanism behind data transfer of Struts2. To understand ValueStack, you must first understand OGNL(Object Graphic Navigation Language). OGNL is used in Struts2 as an expression language. It can be used in the tag library of JSP, so that it can easily access the properties of various objects. It is used by the front-end to pass parameters to the Action (and doing type conversion); It can also be used in struts2 configuration files! Therefore, it is very important to understand the basic mechanism of OGNL.

Root Object

OGNL is called object graph navigation language. An object graph is one that takes any object as its root and allows OGNL to access other objects associated with that object. Such as:

package com.go2fullstack.struts2.actions;
 
public class User {
    private String username;
    private Group group;
   
    public String getUsername() {
       return username;
    }
    public void setUsername(String username) {
       this.username = username;
    }
   
    public Group getGroup() {
       return group;
    }
    public void setGroup(Group group) {
       this.group = group;
    }
}
package com.go2fullstack.struts2.actions;
 
public class Group {
    private String name;
    private Organization org;
    public String getName() {
       return name;
    }
 
    public void setName(String name) {
       this.name = name;
    }
 
    public Organization getOrg() {
       return org;
    }
 
    public void setOrg(Organization org) {
       this.org = org;
    }
}
package com.go2fullstack.struts2.actions;
 
public class Organization {
    private String orgId;
 
    public String getOrgId() {
       return orgId;
    }
 
    public void setOrgId(String orgId) {
       this.orgId = orgId;
    }
}

The above three classes describe that through a User object, you can navigate to the Group object and then to the Organization object. The User object is taken as the root, and an object diagram is shown as follows:

User(root)
   -- username
   -- group
      -- name
      -- org
         -- orgId

In the real environment, this object graph might be extremely complex, but with the basic getters method, you should be able to access other associated objects. Object map navigation, must be navigated through the getters method.

The following code creates a User object and its associated set of objects:

       User user = new User();
       Group g = new Group();
       Organization o = new Organization();
        o.setOrgId("ORGID");
       g.setOrg(o);
       user.setGroup(g);

If you navigate through JAVA code (depending on the getters method), navigate to the Organization’s orgId property, as shown below:

user.getGroup().getOrg().getOrgId();

[note: the purpose of navigation is to get the value of an object or to set the value of an object or to call a method of an object!]
[note: the real purpose of the OGNL expression language is to execute JAVA code where it is not possible to write JAVA code, or to make it easier to execute JAVA code]


The code for navigation with OGNL is as follows:

String value = (String)Ognl.getValue("group.org.orgId", user);

The first argument to the ognl.getvalue () method is an Ognl expression, and the second argument specifies the root object to be used in the expression!
The complete code is as follows:

    public void testOgnl01() throws Exception{
       User user = new User();
       user.setUsername("Ryan");
      
       //Access the user object's username property using an OGNL expression
       String value = (String)Ognl.getValue("username", user);
       log(value);
    }
   
    public void testOgnl02() throws Exception{
       User user = new User();
       Group g = new Group();
       Organization o = new Organization();
       o.setOrgId("ORGID");
       g.setOrg(o);
       user.setGroup(g);
      
       //Access using Java code
       log(user.getGroup().getOrg().getOrgId());
      
       //Access using OGNL expressions
       String value = (String)Ognl.getValue("group.org.orgId", user);
       log(value);
    }
   
    public void testOgnl03() throws Exception{
       User user = new User();
       Group g = new Group();
       Organization o = new Organization();
       o.setOrgId("ORGID");
       g.setOrg(o);
       user.setGroup(g);
      
       //Access using Java code
       log(user.getGroup().getOrg().getOrgId());
      
       //You can also use #root in expressions to represent root objects
       String value = (String)Ognl.getValue("#root.group.org.orgId", user);
       log(value);
    }
    private void log(Object o){
       System.out.println(o);
    }

Context Object

In the expression of OGNL, it may be necessary to access multiple unrelated objects. In this case, we need to pass a Map type object to OGNL and put the objects needed in the expression into the Map. This Map object is called the context.
To access objects in the context in an expression, you need to use the syntax rule “#object name.”
Such as:

    public void testOgnl04() throws Exception{
       User user = new User();
       user.setUsername("Jenny");
       Group g = new Group();
       Organization o = new Organization();
       o.setOrgId("ORGID");
       g.setOrg(o);
       user.setGroup(g);
      
       User user2 = new User();
       user2.setUsername("Denil");
      
       /**
        * The context is really just an object of type Map. Mainly because multiple root objects are not supported in OGNL, then
        * if you need to access more unrelated objects in an expression, you can only pass them to OGNL through a Map.
        */
       Map context = new HashMap();
       context.put("u1", user);
       context.put("u2", user2);
      
       //The object in the context is accessed through the "#name of the object" in the expression
       //If the root object is not used in the expression, you can use any object to represent the root object!
       String value = (String)Ognl.getValue("#u1.username + ',' + #u2.username", context,new Object());
       log(value);
    }
   
    public void testOgnl05() throws Exception{
       User user = new User();
       user.setUsername("Jenny");
       Group g = new Group();
       Organization o = new Organization();
       o.setOrgId("ORGID");
       g.setOrg(o);
       user.setGroup(g);
      
       User user2 = new User();
       user2.setUsername("Denil");
      
       User user3 = new User();
       user3.setUsername("Masks");
      
       Map context = new HashMap();
       context.put("u1", user);
       context.put("u2", user2);
      
       //Pass the root object and context object to OGNL to interpret the corresponding expression
       String value = (String)Ognl.getValue("#u1.username + ',' + #u2.username + ',' + username", context,user3);
       log(value);
    }

OGNL expression is used for assignment

OGNL expressions can also be used for assignment operations.

    public void testOgnl06() throws Exception{
       User user = new User();
      
       //calls the setValue() method for the assignment
       //first parameter: OGNL expression
       //second parameter: root object
       //third parameter: the value to assign  
       Ognl.setValue("username", user, "ZS");
 
       log(user.getUsername());
    }
   
    public void testOgnl07() throws Exception{
       User user = new User();
      
       Map context = new HashMap();
       context.put("u", user);
      
       //calls the setValue() method for the assignment
       //first parameter: OGNL expression
       //second parameter: context object
       //third parameter: root object
       //fourth parameter: the value to assign
       Ognl.setValue("#u.username", context, new Object(), "ZS");
 
       log(user.getUsername());
    }
   
    public void testOgnl08() throws Exception{
       User user = new User();
      
       Map context = new HashMap();
       context.put("u", user);
       
       //use the assignment symbol "=" for assignment
       Ognl.getValue("#u.username = 'LS'", context, new Object());
 
       log(user.getUsername());
    }
   
    public void testOgnl09() throws Exception{
       User user1 = new User();
       User user2 = new User();
       Map context = new HashMap();
       context.put("u1", user1);
       context.put("u2", user2);
      
       //multiple expressions can be executed simultaneously in an expression separated by commas
       Ognl.getValue("#u1.username = 'LS',#u2.username='WW'", context, new Object());
 
       log(user1.getUsername());
       log(user2.getUsername());
    }

Calls methods on objects using OGNL

    public void testOgnl10() throws Exception{
       User user = new User();
      
       //If you are calling a method of the root object, you can call the method directly using the name of the method
       Integer value = (Integer)Ognl.getValue("addSomething(1,1)", user);
       log(value);
    }
   
    public void testOgnl11() throws Exception{
       User user = new User();
       user.setUsername("LS");
       //If you are calling a method of the root object, you can call the method directly using the name of the method
       String value = (String)Ognl.getValue("getUsername()", user);
       log(value);
    }
   
    public void testOgnl12() throws Exception{
       User user = new User();
       Ognl.getValue("setUsername('WW')", user);
       String value = (String)Ognl.getValue("getUsername()", user);
       log(value);
    }
   
    public void testOgnl13() throws Exception{
       User user = new User();
       user.setUsername("WW");
       //visit static variable
       //note:out is a static variable in System class,it is an object of type PrintStream
       //And println() is out's instance method(not static method)
       //call static method, you need '@' symbol, but instance method just a '.' symbol.
       Ognl.getValue("@System@out.println(username)", user);
    }
   
    public void testOgnl14() throws Exception{
       User user = new User();
       user.setUsername("wangwu");
       //You need to use full path Class name when calling static method    Ognl.getValue("@System@out.println(@cn.com.leadfar.utils.Utils@toUpperCase(username))", user);
    }

Access arrays and Collection with OGNL

    public void testOgnl15() throws Exception{
      
       Object root = new Object();
       Map context = new HashMap();
      
       //create an object of type java.util.List
       List list = (List)Ognl.getValue("{123,'xxx','kdjfk'}", context, root);
       context.put("list", list);
      
       //create array
       int[] intarray = (int[])Ognl.getValue("new int[]{23,45,67}", context, root);
       context.put("intarray", intarray);
      
       //create an object of type java.util.Map
       Map mapvalue = (Map)Ognl.getValue("#{'listvalue':#list,'intvalue':#intarray}", context, root);
       context.put("mapvalue", mapvalue);
      
       //access arrays and collection object
       Ognl.getValue("@System@out.println(#list[1])", context,root);
       Ognl.getValue("@System@out.println(#intarray[2])", context,root);
       Ognl.getValue("@System@out.println(#mapvalue.listvalue[0])", context,root);
       Ognl.getValue("@System@out.println(#mapvalue['intvalue'][0])", context,root);
    }
   
    public void testOgnl16() throws Exception{
      
       List root = new ArrayList();
       User user1 = new User();
       user1.setUsername("ZS");
       User user2 = new User();
       user2.setUsername("LS");
       root.add(user1);
       root.add(user2);
      
       //If root object is type of java.util.List
       log(Ognl.getValue("#root[0].username", root));
       log(Ognl.getValue("#root[1].username", root));
    }

More features, please refer to the official documents :
http://commons.apache.org/proper/commons-ognl/language-guide.html

ValueStack

ValueStack is actually the encapsulation of OGNL, the main function of OGNL is to get value and set value, Struts2 is to get and set value by ValueStack!

https://go2fullstack.com/wp-content/uploads/2020/05/image-6-1024x426.png

ValueStack is an interface, and OgnlValueStack is the default implementation in strtus2. The data in ValueStack is stored in two parts: root and context (this is consistent with the concept in OGNL), while ValueStack exposes the associated methods:

void setValue(String expr, Object value);
Object findValue(String expr);

The root object in ValueStack is CompoundRoot, which inherits the ArraryList and provides additional methods: push() and pop() to access the data contained in the root object!

public class CompoundRoot extends ArrayList {
 
    public CompoundRoot() {
    }
 
    public CompoundRoot(List list) {
        super(list);
    }
 
 
    public CompoundRoot cutStack(int index) {
        return new CompoundRoot(subList(index, size()));
    }
 
    public Object peek() {
        return get(0);
    }
 
    public Object pop() {
        return remove(0);
    }
 
    public void push(Object o) {
        add(0, o);
    }
}

It is through these two methods that CompoundRoot becomes a Stack structure! The Stack’s push operation causes object to be placed on the 0th element of CompoundRoot (the 0th element is the top of the stack) and other objects to be moved back in order. The Stack’s pop operation will cause the 0th element of CompoundRoot to be removed (that is, the top element of the stack will be popped out), and the other objects will be moved forward one by one!
 
While OGNL does not support multiple root objects, struts2 can support multiple root objects and extends OGNL.
If an OGNL expression is passed to ValueStack through the setValue or findValue method of ValueStack and the expression contains access to the root object, ValueStack will search the CompoundRoot object from the top of the stack to the bottom of the stack for the object contained in the CompoundRoot object, see which object has the corresponding property, find it, execute it, and return immediately.
 
In Struts2, the Action object itself is pushed into ValueStack (in effect, into ValueStack’s CompoundRoot) before a request finally reaches the Action method, so the Action object is an element in CompoundRoot. Look at the following code:

public class UserAction {
    private String username;
    private Integer age;
    private boolean valid;
   
    //Check user's detail information
    public String detail(){
      
       //set value for this action instance's property
       username = "ZS";
       age = 18;
       valid = true;
      
       return "detail";
    }
// ..... ignore the other code, including the getters and setters

Here is the ‘detail’ page:

username:<s:property value="username"/> <br/>
valid:<s:property value="valid"/> <br/>
age:<s:property value="age"/> <br/>

The above JSP pages will fetch their values correctly. <s:property value=”OGNL Expression“/> . The OGNL expression in the s:property tag is eventually handed over to ValueStack to interpret. The username is an OGNL expression that calls the getUsername() method of the root object. Struts2 will automatically search for elements in CompoundRoot (starting with the 0th element) and detect whether those elements have a getUsername() method. If the 0th element does not have a getUsername() method, it will continue to search for elements 1, 2, 3… whether an element has a getUsername() method.
 
In the example above, there is only one object in CompoundRoot, the userAction object, and that object happens to have the getUsername() method, so the JSP code above will be able to fetch the value correctly.

Another example:

public class UserAction {
    private String username;
    private String name;
   
    public String detail(){
       username = "Action's Username Property";
       name = "Action's Name Property";
      
       User u = new User();
       u.setUsername("User's Username Property");
       ActionContext.getContext().getValueStack().push(u);
      
       return "detail";
    }

In the UserAction code above, we directly call the actioncontext.getContext ().getValueStack ().push() method, and push a User object which has getUsername() and setUsername() methods directly into ValueStack. In this case, there will be two elements in CompoundRoot of ValueStack: The 0th element is the user object just pushed in, while the 1th element is the userAction object. If the following expression is used in the JSP to evaluate:
< s: property value = “username” / >, then the output value will be “User’s Username Property”! As explained above, struts2 will search for objects in CompoundRoot from the 0th element, which is the user object that was just pushed in!
If the expression is <s:property value=”name”/> in the JSP, the “Action’s Name Property” will be taken out, because the 0th element user object does not have a name property, so the search will continue to the 1th element userAction object, in this object there is a name property!

More example:

public class UserAction {
    private String username;
   
    public String detail(){
       username = "UserAction's Username Property";
      
       List list = new ArrayList();
       for(int i=0; i<10; i++){
           User user = new User();
           user.setUsername("User"+i);
           list.add(user);
       }
       ActionContext.getContext().put("users", list);
      
       User u = new User();
       u.setUsername("User's Username Property");
       ActionContext.getContext().getValueStack().push(u);
      
       return "detail";
    }

And the JSP page is:

1:  <s:property value="username"/> <br/>
2:  <s:iterator value="#users">
3:     <s:property value="username"/>
4:     <s:property value="#root[2].username"/><br/>
5:  </s:iterator>
6:  <s:property value="username"/>
7:  <s:property value="#root[1].username"/> <!-- UserAction's Username Property -->

Based on the previous example, we know that the username in line 1 is “User’s Username Property” (since the JSP is executing this line, there are two elements in CompoundRoot: the 0th element is “user object”, and the 1st element is “userAction object”), so the username in line 1 will fetch the username attribute of the 0th element in CompoundRoot: User’s Username Property
 
Line 2 is the iterator tag, with only one value property defined. The iterator tag will loop to the User object in the List of users and push the User object of the current loop into CompoundRoot! So, when lines 3 and 4 are executed, there are a total of three elements in CompoundRoot: the 0th element is the user object of the current loop that is pushed into by the iterator tag; The 1th element is “user object”; The 2th element is “userAction object”, so the result of the execution of the third line of code is the output “UserX”, which is the username attribute of the user object in the current loop! The iterator tag will fetch the user object from the List in turn and keep pushing/popping the user object (pushing/popping is done again each time through the loop). The fourth line takes the username property of the 2th element, which is the username property of the userAction object: UserAction’s Username Property.
 
After line 5 is executed, you will have two elements left in CompoundRoot, just as you did before line 2 was executed. So the output of line 6 is the same as the output of line 1, while line 7 takes the username property of the userAction object: UserAction’s Username Property.

Original Publish: https://blog.csdn.net/li_tengfei/article/details/6098134

Leave a Reply

textsms
account_circle
email

Go2FullStack

ValueStack, the data transfer mechanism behind Struts2
Ognl, ValueStack Basic ValueStack is the mechanism behind data transfer of Struts2. To understand ValueStack, you must first understand OGNL(Object Graphic Navigation Lang…
Scan QR code to continue reading
2010-12-25