How can we analyze the source code? Of course, I'm not sure. I think it may be divided into several layers. writing and writing comments is the most basic. Writing key ideas and difficulties is not bad, but even more difficult is to jump out of the source code, form your own train of thought. Work hard.
This is for jforum2.1.8. Probably the jforum team has no idea about this version, and all of them are directed at jforum3. This version is selected mainly because jforum is a leader in Java Forum applications. Many people use this version for secondary development, while jforum3 uses another architecture, it is not completely release, so we should choose this classic version for consideration.
There are already a lot of introductions about jforum online. Here we also simply copy a section: jforum is a powerful and easy-to-manage forum. Its design fully complies with the MVC design pattern and can run on any servlet container and EJB server. Moreover, jforum forums can be easily customized and expanded.
The above section is pertinent. In addition, jforum imitates phpBB and uses the classic-blue style. However, you cannot select the style yourself. You can only modify the style yourself.
In other words, jforum is excellent because there are few excellent Java Open-Source Forum series, and there are a lot of jforum bugs. If you don't believe it, you will find it. However, as a molding component, it is powerful and suitable for secondary development, and should be included in the scope of consideration.
In any case, jforum is a good learning model. At least it makes you feel that it is not difficult to write a shanzhai framework, and the fact is true. The important thing is, don't take it out easily to harm people. :) here we will list some possible analysis points:
Web. xml
Initialization Process
Request Processing Process (MVC)
File monitoring
Cache implementation
Database Access Implementation
Permission Control
First, understand a web application, and first know the processing process. First, let's take a look at the portal web. XML, the content inside is quite clear. We can see that there is a listener forumsessionlistener ,*. page filter clickstreamfilter. There are two more *. page processor, where installservlet is installed and jforum is the frontend processor. Basically, the entire process is client request-> forumsessionlistener-> clickstreamfilter-> jforum-> server response.
Forumsessionlistener implements the httpsessionlistener interface, but only processes session destory. In this process, the session history is saved to the database and user information and related security information are cleared.
Clickstreamfilter implements the filter interface, and the main task is handed over to the botchecker, which is used to check whether the client is a robot.
The main work is still on jforum. But let's take a look at how jforum detects robot?
The botcheckeronly has a static tool named javasisbot. first, it checks the role request robot.txt (which is a standard robot protocol file), then judges the User-Agent header, and finally judges the remotehost. The known robot is written in the file clickstream-jforum.xml (including agent and host), and loaded through configloader (in the sax mode ).
We can see that both jforum and installservlet inherit the httpservlet jforumbaseservlet, and jforumbaseservlet includes two important methods: init and startapplication. As we all know, init is called during servlet initialization. The process of init in jforumbaseservlet is as follows:
Call the init of the parent class (normally this is required)-> Configure log4j-> startsystemglobals (load global parameter configuration systemglobals. properties-> load database configuration database. driver. config (such as MySQL is WEB-INF/config/database/MySQL. properties)-> load custom configuration (jforum-custom.conf by default)-> Configure Cache Engine-> Configure freemarker template engine-> load module configure modulesmapping. properties-> load URL ing configuration urlpattern. properties-> load the i18n configuration (ages/*)-> load the page ing configuration (templatesmapping. properties)-> load the bbcode configuration bb_config.xml-> end
Jforum implements its own MVC. The entire MVC context is client request-> URL (urlpattern. properties), get module/Action/param-> get the corresponding module class through module, identify and call the corresponding method through action (modulesmapping. properties)-> use DAO to complete business logic-> call template for rendering (templatesmapping. properties), in fact, the entire MVC and struts are no different, the specific process will be mentioned later.
The startapplication method in jforumbaseservlet is as follows:
Load SQL statements of common SQL files. queries. driver (/database/generic/generic_queries. SQL)-> load a specific SQL file (for example, MySQL is/database/MySQL. SQL)-> load quartz scheduled task configuration-> load login validators (Verification Method) -> load DAO implementation-> Load file modification listener-> load query index Manager-> load scheduled statistics tasks
Jforum implements its own Orm, not hibernate, but SQL mapping similar to ibatis, and provides multiple sets of SQL files to implement database-independent features, the entire process is clear. Load database configuration-> load SQL Mapping File-> set DAO implementation-> use named SQL to find the corresponding SQL (in *. in the SQL statement)-> run the data
Continue to focus. The jforum INIT process is as follows:
Jforumbaseservlet. init-> jforumbaseservlet. startapplication-> Start database-> pre-load some data into the cache (forumrepository [categories, forums, maximum number of online users at the same time, last login user, number of registered users, etc.], user level, emoticon data, blocked list)-> end
The above briefly mentioned the jforum request processing process. Now let's take a look at this process, which is the service method. Code Overview:
// Initialize jforumexecutioncontext
Jforumexecutioncontext EX = jforumexecutioncontext. Get ();
// Encapsulate request and response
Request = new webrequestcontext (req );
Response = new webresponsecontext (RES );
// Check the database status
This. checkdatabasestatus ();
// Create jforumcontext and set it to jforumexecutioncontext
.......
Jforumexecutioncontext. Set (Ex );
// Refresh the session
Utils. refreshsession ();
// Attach User Permissions
Securityrepository. Load (sessionfacade. getusersession (). getuserid ());
// Context required by the preload Template
Utils. preparetemplatecontext (context, forumcontext );
// Parse the module name from the request
String module = request. getmodule ();
// Module name-> module class
String moduleclass = module! = NULL? Modulesrepository. getmoduleclass (module): NULL;
// Determine whether it is in the ban list
......
Boolean shouldban = This. shouldban (request. getremoteaddr ());
// Lead
Out = This. processcommand (Out, request, response, encoding, context, moduleclass );
// Scan the tail, for example, the rollback of the DB
This. handlefinally (Out, forumcontext, response );
Processcommand calls the process method of command:
// Obtain a module instance (inheriting the command)
Command c = This. retrievecommand (moduleclass );
// Enter Process
Template template = C. Process (request, response, context );
// The process method starts here.
// Obtain the action
String action = This. Request. getaction ();
// Call this action if it is not ignore
If (! This. ignoreaction) {This. getclass (). getmethod (action, no_args_class). Invoke (this, no_args_object );}
// If it is forwarded, The templatename is cleared.
If (jforumexecutioncontext. getredirectto ()! = NULL) {This. settemplatename (templatekeys. Empty );}
// If no forwarding is performed and template exists in the attribute, set it to templatename.
Else if (request. getattribute ("template ")! = NULL) {This. settemplatename (string) request. getattribute ("template "));}
// Is it coustomcontent? For example, downloading the verification code subclass does not require page operations.
If (jforumexecutioncontext. iscustomcontent () {return NULL ;}
// Return a template
Return jforumexecutioncontext. templateconfig (). gettemplate (
New stringbuffer (systemglobals. getvalue (configkeys. template_dir )).
Append ('/'). append (this. templatename). tostring ());
}
// Return from process to processcommand
// Set content type
Response. setcontenttype (contenttype );
// Generate the page and flush
If (! Jforumexecutioncontext. iscustomcontent ()){
Out = new bufferedwriter (New outputstreamwriter (response. getoutputstream (), encoding ));
Template. Process (jforumexecutioncontext. gettemplatecontext (), OUT );
Out. Flush ();
}
}
This is a general process, just like the customcontent mentioned above, which is to be handled by yourself. For details, refer to captchaaction. Generate ().
In this case, if we want to add some actions for secondary development, the general process is to add a class that inherits the command, such as exampleaction, to define a method, for example, test () in urlpattern. properties defines a ing, for example, example. test.1 = forum_id, and then in modulesmapping. properties defines the module class ing, such as example = exampleaction. properties defines the ing of templates, for example, example. test = example_test.htm. Assume that the request URL is/example/test/1. Let's take a look at some methods in test:
This. Request. getintparameter ("forum_id") // obtain the parameter and obtain 1
This. Context. Put ("OBJ", OBJ); // write the result to context, which can be obtained in the template.
This. settemplatename ("example. test"); // you can specify the Template Name.
Should such a simple process be better understood?
In addition, we can also see that jforum uses its own set of ing mechanism, which is through urlpattern. properties (refer to the INIT process of jforumbaseservlet above). This is implemented in the first line of the loadconfigstuff method of jforumbaseservlet and loaded to urlpatterncollection, as shown below:
Properties P = new properties ();
FS = new fileinputstream (systemglobals. getvalue (configkeys. config_dir) + "/urlpattern. properties ");
P. Load (FCM );
for (iterator iter = P. entryset (). iterator (); ITER. hasnext ();) {
map. entry entry = (map. entry) ITER. next ();
urlpatterncollection. addpattern (string) entry. getkey (), (string) entry. getvalue ();
}< br> the key and value here are both string
urlpatterncollection. patternsmap. put (name, new urlpattern (name, value);
However, In the addpattern method, a urlpattern is actually generated as the value. You can refer to the Code for constructing a urlpattern, for example, for exampl E. hello.2 = A, B, which generates a urlpattern with the name as example. hello.2, value is A, B. the size and vars are parsed by A and B to indicate the total number of parameters and an array composed of parameter names. Therefore, urlpattern stores the definition of a URL format, and a series of URL ing formats placed in urlpatterncollection are used during request URL parsing.
Now let's analyze how jforum uses the urlpatterncollection? According to our non-strict thinking, it should be the service processing URL, get. part before page, such as/example/Hello/2/1, split with/to get Module name and action name. Use module, action, and, the number of parameters constitutes a key (example. hello.2), find the corresponding urlpattern through urlpatterncollection, and add the parameters to the request parameters through the corresponding format (the parameter name and URL parameter value in vars. The actual situation is similar. When talking about the service method in jforum, we mentioned that request and response are packaged:
Request = new webrequestcontext (req );
Response = new webresponsecontext (RES );
Webresponsecontext is just a simple delegate to httpservletresponse (the advantage is that all methods are restricted to responsecontext), while webrequestcontext inherits httpservletrequestwrapper and implements the requestcontext interface. Therefore, webrequestcontext is an httprequest, but some specific methods are implemented through the requestcontext interface, such as getmodule/getaction. The URL parsing process is implemented during the construction of the webrequestcontext object. Let's take a look at the webresponsecontext constructor. I will not detail it here. Note that all parameters are stored in the query (a private map. There is also the specific URL ing mechanism of jforum mentioned above, which is implemented through the parsefriendlyurl method of webrequestcontext. The principle is not detailed as mentioned above.
At this point, the entire processing process is almost the same. Now let's talk about the file modification listener in jforum (startapplication process of jforumbaseserver). If you modify some files during jforum usage, such *. SQL, jforum will re-load the modified configuration. I thought it was implemented using the quartz framework. Later I realized it was implemented using the JDK timertask class. See the listenforchanges method of configloader:
Filemonitor. getinstance (). addfilechangelistener (New queriesfilelistener (),
Systemglobals. getvalue (configkeys. SQL _queries_generic), filechangesdelay );
Here we will share the responsibilities of each part. filemonitor is the manager responsible for managing all the file listeners. filechangelistener is a listener interface with only one method, that is, filechanged (string filename ), it indicates the response to the modification of a filename. The method is also very simple, that is, to implement a filechangelistener, and with the monitored file name, check the interval as the parameter input to take effect. The implementation principle in filemonitor is to save (file name/timertask) through a map (timerentries). Every time a listener is added, it will first remove the original file listener Based on the file name (the disadvantage is that only one listener can be added to a file), and then build a timertask and add it to timerentries. For details about how to use timertask, refer to the API.
as a forum, caching at the application layer seems essential, and jforum also provides cache configuration (as mentioned above ). Jforum provides several cache implementations (jforumbaseservlet INIT process), namely defaultcacheengine (simple memory implementation), jbosscacheengine, and ehcacheengine ., See the startcacheengine method of configloader. The process is probably to get the implementation configuration of cacheengine (systemglobals. configure cache in properties. engine. implementation), then generate the cacheengine instance, call its init Method for initialization, and find all the cacheable classes (implemented the cacheable interface, and in systemglobals. configure cacheable in properties. objects), and finally injects the cacheengine into it to obtain the cache capability. Although jforum has implemented many such injections (in addition to cacheengine, DB, Dao, and so on), although it has achieved some purpose, however, the implementation of Singleton is everywhere (see spring2.5 document 3.9. bond code and terrible Singleton), in order to find a better way of organizing (for example, using IOC to manage objects and using mature ORM to isolate databases) and get more user groups (select more widely used framework help), probably will sprout the idea of jforum3.
By the way, let's take a look at the jforum DAO implementation method (refer to the startapplication process of jforumbaseservlet). Refer to the loaddaoimplementation method of configloader. The principle is to configure Dao. driver (such as MySQL in a specific database configuration. properties) Get the dataaccessdriver implementation-> initialize dataaccessdriver-> get all DaO implementations. It can be understood that implementing a dataaccessdriver gets a complete set of DAO implementation methods. For the implementation methods in Dao, an example is provided:
// Routine
Preparedstatement P = NULL;
Resultset rs = NULL;
// Obtain connect and execute named SQL
P = jforumexecutioncontext. getconnection (). preparestatement (systemglobals. getsql ("groupmodel. selectbyid "));
P. setint (1, groupid );
Rs = p.exe cutequery ();
Group G = new group ();
// Process the cyclic resultset
If (Rs. Next () {G = This. getgroup (RS );}
The entire implementation is straightforward, that is, a JDBC implementation method. For how to obtain the connection and view the getconnection () of jforumexecutioncontext, You can note the following sentence:
C = dbconnection. getimplementation (). getconnection ();
It is also clear. In addition, you can know that in each request process, the connection will only be obtained once and put in threadlocal after obtaining the information for the first time, in this way, a copy of data (correct understanding of theradlocal) is retained in each thread, and the connection (handlefinally method in the service process) is released after the request ends ).
Jforumexecutioncontext, literally, is the context of request execution, such as the database connection mentioned above, as well as forumcontext (with information related to request and response), context (context variable of freemarker ), redirectto (forwarding address), contenttype (Response content format), iscustomcontent (no default rendering is used, as mentioned above), enablerollback (whether the DB will roll ).
Jforum allows you to configure permissions. The controllable permission types are stored in securityconstants. The corresponding configuration interface is generated based on permissions. XML (refer to permissions of groupaction ). The permissioncontrol of each user is managed through securityrepository. The most useful permission system is the role (permission)-group (user group, multi-level)-user structure.
How to determine permissions?
For a user, to obtain the user's permission (permissioncontrol), the process is as follows (For details, refer to the securityrepository load method ): get user information-> get all the user's groupid and form a comma-separated string groupids-> get all names/role_value Based on groupids-> assemble them into rolevaluecollection-> Generate rolecollection-> finally, permissioncontrol is generated.
To determine the permission, use the canaccess (INT userid, string rolename, string value) method of securityrepository:
Obtain permissioncontrol Based on userid-> If the value parameter is null, determine whether the rolename exists (using the keys of the internal rolecollection object ), is whether the permission is contained-> If the value parameter is not empty, in addition to the permission, it also has the corresponding rolevalue (through the values of the internal rolecollection object ). The value index in the parameter can be the forum category ID or forum ID, depending on the business.
In general, jforum is still clear, and most of the Business Code is not carefully looked at (those command classes). If you are interested, you can compare and write the code, which is roughly divided into three packages (Admin is the management, jforum is a public page, and install is an installation page ).
As for verification, let's talk about jforum's SSO verification mechanism.
Official documentation:
Http://www.jforum.net/doc/SSO
Http://www.jforum.net/doc/ImplementSSO
Http://www.jforum.net/doc/SSOcookies
Http://www.jforum.net/doc/SSOremote
You can basically implement one of the above documents by yourself, mainly implementing the net. jforum. SSO interface.
there is a segment in the jforum service method (refresh session in the service process):
controllerutils utils = new controllerutils ()
utils. refreshsession (); // highlights
it is mentioned that if there is no usersession, if the configured authentication type is SSO (authentication. type), you can call the checksso (usersession) method
-> to generate an SSO instance (using SSO. implementation to configure)-> call authenticateuser (requestcontext request) to return username
-> If the username cannot be obtained, set it to anonymous-> otherwise, if the user does not exist (utils. userexists (username) is registered One (utils. register (password, email)-> if it already exists, log on to the user (configureusersession (usersession, utils. getuser ()
when usersession already exists and the authentication method is SSO, it is used to verify whether the authentication is valid (SSO. issessionvalid (usersession, request )).
therefore, the entire process is the same as the process mentioned in the official document. If you want to implement your own SSO, This Is The SSO interface, and authenticateuser is used to verify that there is no usersession, return username or null, and use issessionvalid to determine whether an existing usersession is valid. Refer to the above connection documents to achieve SSO integration with existing systems, which is clear and clear.