This framework will be very easy understanding if you are familiar with Java Reflection.
It just do four things:
- Construct argument values of the method you written, by getting them from ObjectSource.
- Invoke the method on the target object.
- Save the method result into ObjectSource.
- Process the URL dispatching.
ObjectSource is the core interface of this framework, for getting and saving objects. The most work you are doing is to tell this framework how to get object from ObjectSource, and save object into ObjectSource.
Executable is another core interface. It can be executed only with an ObjectSource.There are two implementations of this interface, one is Invoke, and another is Action. They both are advanced encapsulation of Java Method object, an Action can have one or more Invokes, and an Invoke are conresponding to a Method.
Resolver is an important concept, it is the target of Invoke's method. But there is no interface defined for it, because it is writing by you. You may call it "Business object".
Converter helps ObjectSource converting the object it stored to the Invoke's method argument type.
The detail structure of SoybeanMilk configuration file is listing bellow:
-
<soybean-milk>
Root element
-
<global-config>
Global configuration block
-
<generic-converter>
Generic Converter
- <converter> Support Converter
- <interceptor> Interceptor
-
<generic-converter>
Generic Converter
-
<includes>
Included modules
- <file> Module file
-
<resolvers>
Resolver configuration block
- <resolver> Resolver
- <executables> Executable configuration block
-
<global-config>
Global configuration block
Global configuration block. It is just defined as a parent tag with no attribute.
<generic-converter> (Optional)
Generic Converter configuration. Type conversion is a nest function of this framework, it works when you want to get an object which has different type with the object stored in ObjectSource (See GenericConverter interface).
Especially in web environment, the request values are all Strings, but the argument of Invoke method may be any type.
It contains the following attributes:
-
class (Optional)
Customized generic converter class with which you can completely replace the nest generic converter. This customized generic converter must be an implementaion of GenericConverter interface.
The nest generic converter WebGenericConverter will be used if you didn't set this attribute.
Support Converter object.
You can add new support converters or replace old converters to enhance the generic converter.
The nested generic converter implementation WebGenericConverter has add most common support converters (see DefaultGenericConverter class), and it also can convert "java.util.Map<String, String>" and "java.util.Map<String, String[]>" to JavaBean object.
It contains the following attributes:
-
src (Required)
The source type it can convert from. Such as "java.lang.String" (String class), "[Ljava.lang.String;" (String[] class). -
target (Required)
The target type it can convert to. Such as "int" (int class), "[I" (int[] class) -
class (Required)
The class of this converter.
The src attribute value here is limited because the object saved in ObjectSource at web environment is limited.
The following are all the src values you can use:
| Value | Note |
| String | Shortname for java.lang.String class |
| String[] | Shortname for java.lang.String[] class |
| request | Shortname for javax.servlet.http.HttpServletRequest class |
| session | Shortname for javax.servlet.http.HttpSession class |
| application | Shortname for javax.servlet.ServletContext class |
| response | Shortname for javax.servlet.http.HttpServletResponse class |
The framework also defines some shortname for target attribute value, The rule is listing below:
-
Primitive type, their wrapper type, and String, BigDecimal, BigInteger, java.util.Date type, also with their array type,
are the same with their Java syntax, and do not need its package prefix, such as :
"int", "Integer", "int[]", "Integer[]", "String", "String[]", "Date[]", "BigDecimal[]". -
The following types
java.sql.Date
java.sql.Time
java.sql.Timestamp
are familiar with above, but package prefix is required, such as :
"java.sql.Time", "java.sql.Time[]".
The following is a support converter example, the nested will be replaced:
<generic-converter> <converter src="String" target="Date" class="your.converter.DateConverter" /> <converter src="String[]" target="Date[]" class="your.converter.DateArrayConverter" /> <converter src="request" target="java.io.File" class="your.converter.FileConverter" /> </generic-converter>
Interceptor configuration. You can add "before", "after", "exception" interceptor for Executor using this configuration.
It contains the following attributes:
-
before (Optional)
Set the "before" interceptor's global Executable name. Executor will execute it first before the current Executable. -
after (Optional)
Set the "after" interceptor's global Executable name. Executor will execute it after correctly executing the current Executable. -
exception (Optional)
Set the "exception" interceptor's global Executable name. Executor will execute it if exception thrown when executing the current Executable. -
execution-key (Optional)
Saving keyword of the execution context object Execution. With this object, you can get the execution context information in your interceptor.
The following is an "exception" interceptor example, the framework will execute Action "/handleException.do" when exception occuring:
<global-config> <interceptor exception="/handleException.do" execution-key="session.execution" /> </global-config> <executables> <action name="/handleException.do"> <invoke method="handleException" resolver="exceptionResolver"> <arg>session.execution</arg> </invoke> <target url="/error.jsp" /> </action> ... ... </executables>
It's corresponding Java method must be:
public void handleException(Execution execution)
{
...
}
Included modules configuration. It is just defined as a parent tag with no attribute.
The file of module configuration. It can be a resource file in classpath, or file in "/WEB-INF" folder of your application.
Note that only <resolvers> and <executables> tags are valid in module configuration files.
It's an example below:
<includes> <file>your/config/module.1.xml</file> <file>your/config/module.2.xml</file> <file>/WEB-INF/config/module.3.xml</file> <file>/WEB-INF/config/module.4.xml</file> <file>/home/yourapp/config/module.5.xml</file> </includes>
Resolver configuration block. It is just defined as a parent tag with no attribute.
Resolver configuration. You may add many resolvers here and using later.
It contains the following attributes:
-
id (Required)
The id of Resolver, and will be used when you defining Executables. -
class (Required)
The class of Resolver. It single instance will be created by the parser of this framework, and using by all referenced Executables.
This is an example below:
<resolvers> <resolver id="yourResolver0" class="your.resolver.MyResolver0" /> <resolver id="yourResolver1" class="your.resolver.MyResolver0" /> <resolver id="yourResolver2" class="your.resolver.MyResolver1" /> </resolvers>
Executable configuration block. It is just defined as a parent tag with no attribute.
Invoke configuration. It can be directly added under the <executables> tag (as global), or added under the <action> tag (as local).
It has tow configuration mode:
It contains the following attributes:
-
name (Optional)
Your must set this attribute if this Invoke is global, and it must be unique together with Action's name attribute. It is optional if this Invoke is local. -
method (Required)
Set the method name of this Invoke, it must be defined in resolver-class or resolver class. The configuration parser will use this attribute to find the Method object. If you set the resolver-class attribute, it's class will be first used for finding, or the resolver attribute's class will be used. -
resolver-class (Alternatively)
Set the Resolver class name of this Invoke. If the Invoke method is static, you can only set this attribute, because static method can be exeucted without instance. -
resolver (Alternatively)
Set the Resolver id of this Invoke defined in <resolvers> tag. All the ID defined in module files can be used here. If the Invoke method is not static, this attribute is required. -
result-key (Optional)
The saving keyword of this Invoke method result. The result will always be saved if you set this attribute, and "null" is saved if the method has no return value. Contrarily, nothing will be saved.
Iegal result keywords are listing below:-
yourResultKey
The result will be saved in "request" scope with keyword "yourResultKey". -
request.yourResultKey
The same as above. -
session.yourResultKey
The result will be saved in "session" scope with keyword "yourResultKey". -
application.yourResultKey
The result will be saved in "application" scope with keyword "yourResultKey".
-
yourResultKey
It makes your configuration for Invoke just like writing Java codes. The syntax is listed below:
<invoke name="yourInvokeName"> resultKey = resolver.method(arg, arg, ...) </invoke>
-
resultKey
The same meaning to the above attribute result-key of <invoke>. -
resolver
The same meaning to the above attribute resolver or resolver-class of <invoke>. The framework can recognise which attribute it is. -
method
The same meaning to the above attribute method of <invoke>. -
arg
The same meaning to the below tag <arg>.
These are some configuration example using Java mode:
<action name="/multiply.do"> <invoke> request.multiplyResult = calculator.multiply( param.a, 100) </invoke> <invoke> your.util.Utils.printToConsole(request.multiplyResult); </invoke> </action>
<invoke name="yourGlobalInvokeName"> request.yourResultKey = yourResolver.yourMethod( param.arg1, true, 'a', '\u00EF', "hello\t hello", 100 ) </invoke>
The argument configuration of an Invoke method. It may be needed only if you are using XML mode to configure your <invoke>.
It contains no attributes, but only content. There are two types of <arg> content, one is keyword, and another is value. The framework can recognize them.
The legal content is listing below:
- Keyword
-
param
The entire request parameter map. If the Invoke method argument is java.util.Map type, then this entire map will be directly passed to the method. And if the argument is other type, the framework will first convert it to the target type, then pass it to the method. -
yourArgKey
The parameters start with "yourArgKey" in request parameter map. The framework will simplely convert it to the Invoke method type and then pass it, if it is explicitly (only one value). Otherwise, the framework will first creat a new map who contains parameters only start with "yourArgKey", and it's key will also be truncated from "beanName.propertyName" to "propertyName". Then, it will directly pass this new map or pass the converted object mentioned as above. -
param.yourArgKey
The same as above. -
request
The HttpServletRequest object. By default, the framework can not convert it. So, you must define your own support converter who converting "javax.servlet.http.HttpServletRequest" source to your destination type if the Invoke method argument is not HttpServletRequest. -
request.yourArgKey
The object whose key is "yourArgKey" in request scope. The framework will not convert it, so the Invoke method argument must be the same type with it. -
session
The HttpSession object. By default, the framework can not convert it. So, you must define your own support converter who converting "javax.servlet.http.HttpSession" source to your destination type if the Invoke method argument is not HttpSession. -
session.yourArgKey
The object whose key is "yourArgKey" in session scope. The framework will not convert it. -
application
The ServletContext object. The framework can not convert it by default. So, you must define your own support converter who converting "javax.servlet.ServletContext" source to your destination type if the Invoke method argument is not ServletContext. -
application.yourArgKey
The object whose key is "yourArgKey" in application scope. The framework will not convert it. -
response
The HttpServletResponse object. The framework can not convert it by default. So, you must define your own support converter who converting "javax.servlet.http.HttpServletResponse" source to your destination type if the Invoke method argument is not HttpServletResponse.
-
param
-
Value
It can only be Java primitive type, String or null with the same syntax as Java language.-
boolean
Boolean value (true or false). The coresponding argument must be "boolean" or "Boolean" type. -
byte
Byte value. The coresponding argument must be "byte" or "Byte" type. -
char
Character. It can be normal character (such as 'a', 'b', 'c', '1', '5'), Java ESC character ('\n', '\r', '\t', '\'', '\\', '\"'), or unicode character ('\uxxxx'). The coresponding argument must be "char" or "Character" type. -
double
Double value. The coresponding argument must be "double" or "Double" type. -
float
Float value. The coresponding argument must be "float" or "Float" type. -
int
Int value. The coresponding argument must be "int" or "Integer" type. -
long
Long value. The coresponding argument must be "long" or "Long" type. -
short
Short value. The coresponding argument must be "short" or "Short" type. -
String
String value. It can contain characters defined in char segment above. The coresponding argument must be "String" type. -
null
Null. The coresponding argument can be any type except primitive.
-
boolean
The following are some examples:
<invoke name="yourGlobalInvoke" method="yourMethod" resolver="yourResolver" result-key="yourResultKey"> <arg>arg1</arg> <arg>param.arg2</arg> <arg>request.arg3</arg> <arg>100</arg> <arg>2.35f</arg> <arg>'a'</arg> <arg>"hello\t hello"</arg> <arg>true</arg> <arg>null</arg> </invoke>
<invoke name="/multiply.do" method="multiply" resolver="calculator" result-key="multiplyResult"> <arg>a<arg/> <arg>param.b<arg/> </invoke>
<invoke name="/doMultiply.do" method="doMultiply" resolver="calculator"> <arg>request</arg> <arg>response<arg/> </invoke>
Action configuration. You can add many <invoke> and <ref> tags into it, and they will be executed orderly.
It contains the following attributes:
-
name (Required)
Set the name of this Action.
It is an example below:
<action name="/yourAction.do"> <invoke method="method0" resolver="yourResolver" result-key="result0" /> <invoke method="method1" resolver="yourResolver"> <arg>param.a<arg/> <arg>request.result0<arg/> <arg>true<arg/> </invoke> <ref name="gloablInvoke0" /> <ref name="gloablInvoke1" /> <target url="/action_result.jsp" /> </action>
Reference configuration. It helps you add one or more global Executable into this Action.
It contains the following attributes:
-
name (Required)
Set the global name of Executable you want to reference.
The target configuration of this Action.
It contains the following attributes:
-
url (Required)
The target URL. It must be start with "/" if it is an URL in your application. -
type (Optional)
The target type. It has only two values : "forward" or "redirect". The framework will treat it as "forward" type if you did not set it.