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 setting 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.lang.reflect.Method object, an Action can have one or more Invokes, and an Invoke are corresponding to a java.lang.reflect.Method object.
Resolver is the basis of Invoke execution, Invoke method execution target is supplied by it, its implementations mostly supply your business object to Invoke.
Converter can convert any object stored in ObjectSource to object that match Invoke's method argument type.
TargetHandler handles the target URL and controls the view type of an Action。
Executor is the entry of this framework, it can find and execute Executables by their names.
The detail structure of SoybeanMilk configuration file is listing bellow:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE soybean-milk PUBLIC "-//SoybeanMilk//DTD soybeanMilk web//EN" "http://soybeanmilk.googlecode.com/files/soybeanMilk-web-1.0.dtd">
-
<soybean-milk>
Root element
-
<global-config>
Global configuration block
-
<generic-converter>
Generic Converter
- <converter> Support Converter
- <interceptor> Interceptor
-
<type-target-handler>
Target handler by type
- <target-handler> Target handler
-
<generic-converter>
Generic Converter
-
<includes>
Included modules
- <location> Module file locations
-
<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.
The nested generic converter implementation WebGenericConverter has add most common support converters (see DefaultGenericConverter class), and it can also convert "java.util.Map<String, Object>" to JavaBean object, JavaBean collection(Array, List, Set) and JavaBean Map.
Support Converter object.
You can add new support converters or replace old converters to enhance the generic converter.
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 framework defines some shortname for src and target attribute value, The rule is listing below:
- "request" means javax.servlet.http.HttpServletRequest class
- "session" means javax.servlet.http.HttpSession class
- "application" means javax.servlet.ServletContext class
- "response" means javax.servlet.http.HttpServletResponse class
-
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="request" target="java.io.File" class="your.converter.FileConverter" /> </generic-converter>
WebGenericConverter has add support converters for converting String to most common atomic types, they all defined in support converters package. Normally, you only need to replace or define support converters for atomic types mentioned above, the WebGenericConverter will automatically decompose conversion to these atomic type conversion.
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. Usually, you only need to handle InvocationExecuteException and ArgPrepareExecuteException in your exception interceptor. -
execution-key (Required)
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> exceptionResolver.handleException(session.execution); </invoke> <target url="/error.jsp" /> </action> ... ... </executables>
It's corresponding Java method must be:
public void handleException(Execution execution)
{
...
}
<type-target-handler> (Optional)
Type target handler configuration. It makes you can define your own target handler for handling action's target by its type(See <target> tag's type attribute).
The framework has supplied a default implementation class DefaultTypeTargetHandler, which can handle "forward" type and "redirect" type target. And you can add more target handler through <target-handler> tag below, or set the class attribute of this tag for completely replacing the default type target handler.
It contains the following attributes:
-
class (Optional)
Set your own type target handler class name, it must implement interface TypeTargetHandler. The default DefaultTypeTargetHandler will be used if you do not set it.
Target handler configuration. It is used for set spefific target handler.
It contains the following attributes:
-
handle-type(Required)
Set the target type this handler can handle, such as "json", "pdf" or "json, pdf, forward". -
class(Required)
Set the class name of this target handler, it must implement the TargetHandler interface. You can also create extension class from AbstractTargetHandler, it supplies very useful methods.
The following is an example of target handler configuration, the nest "redirect" target handler will be replaced:
<type-target-handler> <target-handler handle-type="redirect" class="your.th.RedirectTargetHander" /> <target-handler handle-type="json" class="your.th.JsonTargetHander" /> </type-target-handler>
Included modules configuration. It is just defined as a parent tag with no attribute.
The location of module configuration file. 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> <location>your/config/module.1.xml</location> <location>your/config/module.2.xml</location> <location>/WEB-INF/config/module.3.xml</location> <location>/WEB-INF/config/module.4.xml</location> <location>/home/yourapp/config/module.5.xml</location> </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 can present more than once in a configuration file.
It contains the following attributes:
-
prefix (Optional)
Set the prefix of all the Executables in this configuration file scope, it will be useful in module configuration files.
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 object class. -
resolver (Required)
Set the Resolver object of this Invoke, it can be the value which defined in resolver tag id attribute above, or a full name of an class, or an object id which is storing in this Invoke current ObjectSource. -
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.
See DefaultWebObjectSource class for detail, because it will be directly passed to its set(...) method.
-
breaker (Optional)
The breaker of this Invoke method, for controlling the executing of this Invoke method. It has two means :-
"true" or "false"
If "true", this Invoke method will not be executed, and will be executed if "false". -
Any other string
This string will be treated as a key in the current WebObjectSource, and if the value of this key is Boolean.TRUE or not null, this Invoke method will not be executed, otherwise this Invoke method will be executed.
-
"true" or "false"
It makes your configuration for Invoke just like writing Java codes. The syntax is listed below:
<invoke name="yourInvokeName"> resultKey = resolver.method(arg, arg(type), ...) </invoke>
-
resultKey
The same meaning to the above attribute result-key of <invoke>. -
resolver
The same meaning to the above attribute resolver of <invoke>. -
method
The same meaning to the above attribute method of <invoke>. -
arg
The same meaning to the below tag <arg>. -
type
The same meaning to the below attribute type of <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>
Note that in Java mode, the name and breaker still have to be written in <invoke> tag's attribute.
The argument configuration of an Invoke method. It may be needed only if you are using XML mode to configure your <invoke>.
It contains the following attrinutes :
-
type (Optional)
Set the type of this argument, its value can be class short-names which mentioned at <converter> above, or a full class name. If set, argument value got from ObjectSource will be converted to this type but not the actual Invoke method while executing.
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
See DefaultWebObjectSource class for detail, because it will be directly passed to its get(...) method.
-
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. You must set its type attribute to be "byte" or "Byte" because its literal value is the same as interger value. -
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. Such as : 3.2397, 1.2345d. The coresponding argument must be "double" or "Double" type. -
float
Float value. Such as : 3.2397f, 1.2345F. The coresponding argument must be "float" or "Float" type. -
int
Int value. The coresponding argument must be "int" or "Integer" type. -
long
Long value. Such as : 12345456L, 12345456l. The coresponding argument must be "long" or "Long" type. -
short
Short value. The coresponding argument must be "short" or "Short" type. You must set its type attribute to be "short" or "Short" because its literal value is the same as interger value. -
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"> multiplyResult = calculator.multiply(a, param.b); </invoke>
<invoke name="/doMultiply.do"> calculator.doMultiply(request, response); </invoke>
The Invoke method argument can be any type, but the framework default can only convert request parameters to these types:
primitive and their wrapper types, BigInteger and BigDecimal, java.util.Date and its sub types, JavaBean, and array type of the above;
or their collection types, Map types, such as :
void genericMethod(List<Integer> list); void genericMethod(List<JavaBeanClass> list, Set<JavaBeanClass> set, Map<String, JavaBeanClass> map); void genericMethod(List<T> list, Set<G> set, Map<String, T> map); void genericMethod(T t, G g); void genericMethod(T[] array);
Note that for type variable "T" or "G", will be thought as Object type, if there is no actual type for it in its class context.
If the framework can not recognize your Invoke method argument type, one solution is to define a support convert, or, another simple solution is to add a Invoke for type conversion before this Invoke:
<action name="/complexArgMethod.do"> <invoke> myArg = myResolver.paramToMyArg(param); </invoke> <invoke> myResolver.complexArgMethod(myArg); </invoke> </action>
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, often the servlet path you want it to process.
You can also define variables in it, for RESTful support. For example:<action name="/user/{userId}/edit"> <invoke> userResolver.edit(userId) </invoke> </action>It will be able to process requests such as "/user/jack/edit" or "/user/tom/edit", and "jack" and "tom" will be put into WebObjectSource with key "userId" by the framework.
It is an example below:
<action name="/yourAction.do"> <invoke> arg1 = yourResolver.method0(); </invoke> <invoke> yourResolver.method1(paramArg0, arg1, true); </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 name of Executable you want to reference.
You must set the full name (with prefix) of the target Executable if it is not in this configuration file scope.
The target configuration of this Action.
It contains the following attributes:
-
url (Optional)
The target URL. It must be start with "/" if it is an URL in your application.
You can also use the keywords the WebObjectSource supported here for dynamic URL (With "{" prefix and "}" suffix). For example:<action name="/user/{userId}/edit"> <invoke> userResolver.edit(userId) </invoke> <target url="/user/{userId}/view" type="redirect" /> </action>The framework will redirect to "/user/jack/view" for request "/user/jack/edit". -
type (Optional)
The target type. It tells the type target handler (see <type-target-handler> tag) which target handler must handle this target.
It will be set to "forward" by the framework if you do not set it here.