BLOG (WebLog) is increasingly popular on the Internet. Many netizens have their own blog, show yourself through Blog, and meet more netizens. The more famous Blog platform is based on ASP.NET. TEXT. However, its logic is all in the form of a stored procedure in the database. Although the stored procedure can greatly improve the efficiency of data operations, the stored procedure itself is a structured program, and it is not possible to play object-oriented power or not to implement code multiplexing.
Therefore, I decided to achieve a BLOG platform, function and interface, and .text, based on multi-layer structure based on J2EE systems, and temporarily named Crystal Blog. The implementation features are: publishing and editing articles; multi-user support; full-text search; RSS support; picture management; SMTP mail sending and other common functions. The interface is as follows:
First, choose platform and framework
Due to the J2EE platform, we are ready to use WebLogic Server 8.1 as a run platform, using WebLogic Workshop8.1 as a development tool. The database selects MS SQL Server 2000 SP3 to create a data repository stored in BLOG store all user data.
Since we don't have a specific database encoding, we will use other database tests later. Before system design, choose an excellent framework to greatly improve development efficiency. Spring is a lightweight J2EE framework. It covers almost all aspects of the JDBC from the background database to the front desk web framework. Also, the various modules of Spring are very loose, and we can use it as a framework of the entire application, or only one of its modules. In addition, Spring is very powerful to make us easily integrate the web end written by Struts, or use Hibernate as the backend O / R mapping solution.
Spring's core thinking is IOC and AOP. Spring itself is a lightweight container, and the EJB container is different. Spring's components are ordinary Java Beans, which makes unit testing can no longer rely on the container, more easily. Spring is responsible for managing all Java Beans components that also support declarative transaction management. We only need to write a Java Beans component, then "assemble" it can be, and the initialization and management of components are completed by Spring, just declaration in the configuration file. The biggest advantage of this approach is that the coupling of each component is extremely loose, and there is no need to implement the Singleton mode itself.
Since the background is used to use the relational data storage data, it is essential to use O / R mapping. Ibatis is another O / R mapping program similar to Hibernate, which is characterized by small, simple configuration, flexible query, and fully complies with our requirements.
In addition to Spring and Ibatis, the third-party components used in Spring and Ibatis include: the Lucene engine for full-text search, used for CommON-file-upload1.0 uploaded for files, used to output RSSLIBJ1.0 RC2 for RSS.
Due to the use of Spring this lightweight frame, there is no need for an EJB server, just the web server is required. Therefore, the system can run on a WebLogic Server, Tomcat, and RESIN, etc. On a web server that supports Servlet and JSP.
Second, the system design
Obviously, the J2EE architecture of the multilayer structure ensures the flexibility and scalability of the system. We still use representation of layer / logic layer / persistent layer three-layer design.
The entire system is based on spring, and the persistence layer adopts DAO mode and IBATIS O / R mapping, encapsulates all database operations; the intermediate layer is a normal JavaBean managed by Spring, using FA? ADE mode; represents the MVC framework provided by Spring. Since Spring is integrated with other frameworks, we use Velocity as View. Since Velocity cannot call Java code, forcibly use the MVC mode instead of embed logic code in View. Third, configure the server
Built a configuration in WebLogic, name Blog, add a data source, named jdbc / blog:
The directory structure of the entire application is as follows:
Crystalblog /
DOC / (store API document)
Report / (store JUnit test results)
SRC / (store Java source program)
Web / (web directory)
| Manage / (Store BLOG Management Page)
| SKIN / (store BLOG interface page)
| UPLOAD / (Picture to store users upload)
| Web-INF /
| Classes / (Store compiled class file)
| LIB / (all JAR files used)
| Search / (Store Lucene's INDEX)
| C.TLD (files required to use JSTL)
| Dispatcher-servlet.xml (Spring Profile)
| Web.xml (standard web configuration file)
blog.war (packaged deployment)
Build.xml (Ant script)
Fourth, write ANT scripts
Ant is a very great tool for executing batch tasks. Use Ant to fully automate compilation, test, packaging, deployment, and generating documents, etc., thereby greatly saving development time. First we put all the .jar files used in the / web / web-inf / lib, then write the Compile task, and the generated class file is placed directly into the web / web-inf / class directory. If the compilation is successful, the unit test is performed, and the result of the unit test is stored in the Report directory in text files. If the test passes, the next step is to pack BLOG.WAR files. Then use the application to the server, copy the contents of the web directory to the% bea_home / user_projects / domains / blogdomain / application / blog / directory. If you want to deploy on Tomcat, copy the entire web directory directly to% Tomcat% / WebApps / Blog /. Finally, if needed, you can generate an API document with Javadoc.
V. System design
Crystal Blog is divided into three-layer structure: the background data persistence layer, using DAO mode; the intermediate logic layer, adopts the FACADE mode; the front-end Web layer, the MVC structure, using JSP as a view.
Design Domain object
The Domain layer is an abstract entity. According to the features we want to achieve, the following entities are designed, they are all ordinary Java Beans: Account: packaged a user, including user ID, user name, password, user settings, etc. Category: Encapsulates a classification, a total of 3 category, which are used to manage Article, Image, and Link, an Account corresponding to multiple category. Article: Package an article, including title, summary, content, etc., a category corresponds to multiple Article.
Feedback: Package a reply, including title, username, url, and content, an Article corresponds to multiple Feedback.
Image: Encapsulate a picture, Image only contains image information (ImageID, Type), and the specific picture is stored in the form of a file uploaded to the server. A category corresponds to multiple images.
LINK: Package a link, and Category is a multi-to-one relationship. There are attributes such as Title, URL, RSS.
Message: Encapsulate a message so that other users can send mail to a user through the system without knowing the Email address.
Finally, in order to uniquely identify each database record, we need a primary key. Automatically incremented primary key generation methods can be used in MS SQL Server and Oracle. However, many databases do not support automatically incremented primary keys, take into account portability, we define a sequence table yourself to generate an incremented primary key. SEQUENCE is available and only 7 records, respectively record the current maximum main key value of Account to the Message object, respectively. When the system is started, SqlConfig is responsible for initializing the Sequence table. Sequencedao is responsible for providing the next primary key, in order to improve efficiency, cache 10 primary keys.
Sixth, configure ibatis
Next, use iBATI to implement O / R mapping. First from
Http://www.ibatis.com Download iBatis 2.0, copy the required JAR file to the web / web-inf / lib / directory. iBATIS uses XML configuration database tables to the mapping of the Java object, first write a SQL-MAP-Config.xml:
XML Version = "1.0" encoding = "UTF-8"?>
PUBLIC "- // ibatis.com//dtd SQL Map Config 2.0 // En"
"
http://www.ibatis.com/dtd/sql-map-config-2.dtd ">>
LazyLoadInabled = "true" maxrequests = "32" Maxsessions = "10" maxTransactions = "5" USSTATEMENTNAMESPACES = "false" /> datasource> transactionManager> SQLMAPCONFIG> Put SQL-MAP-Config.xml in a web / web-inf / class / directory, iBATIS can search this configuration file and write a initialization class: Public class sqlconfig { Private sqlconfig () {} Private static final sqlmapclient SQLMAP; STATIC { Try { Java.io.Reader Reader = Resources.getResourceceasReader ("SQL-MAP-Config.xml); SQLMAP = SQLMAPCLIENTBUILDER.BUILDSQLMAPCLIENT (Reader); } catch (exception e) { E.PrintStackTrace (); Throw new runtimeException ("Error Initializing Sqlconfig. Cause:" E); } } Public static sqlmapclient getsqlmaPinstance () { Return SQLMAP; } } SQLMapClient encapsulates most of the operations of accessing the database, you can use SQLConfig.getsqlmapInstance () to get this unique instance. Seven, use DAO model 1 To separate logic layers and database persistence layers, define a series of DAO interfaces: Accountdao, Categorydao, ArticleDao ... Its acting class corresponds to SQLMapAccountDao, SqlmapcategoryDao, SQLMapArticleDao ... This makes the logical layer completely detached the database access code. If you need to use other O / R mapping solutions in the future, you can directly implement the new DAO interface to replace existing SQLMapxxxxdao. Take SQLMapAccountDao as an example to achieve a login () method is very simple: Public int login (string username, string password) throws authorizationException { Try { Map map = new hashmap (); Map.put ("UserName", UserName; Map.Put ("Password", Password); Integer i = (Integer) SQLMap.QueryForObject ("Login", Map); IF (i == null) Throw New RuntimeException ("Failed: Invalid UserName or Password."); Return I.intValue (); } Catch (SQLException SQLE) { Throw new runtimeException ("SQL Exception:" SQLE); } Define login queries in an Account.xml configuration file: SELECT [Accountid] from [Account] Where [username] = # username # and password = # Password # select> Eight, logical layer design Since the DAO mode has implemented all database operations, the business logic is primarily checking, calling the DAO interface, so business logic is a simple FACADE interface: Public class facadeimpl imports facade { PRIVATE ACCOUNTDAO Accountdao Private articledao articledao; Private categorydao categorydao; Private feedingbackdao feedbackdao; PRIVATE ImageDAO imageDAO PRIVATE LINKDAO LINKDAO; Private sequencedao sequencedao; } For ordinary getArticle (), FACADE simply calls the corresponding DAO interface: Public Article getArticle (int ArticleID) throws queryexception { Return articledao.getArticle (articleid); } For operations that require authentication, such as deleteArticle () methods, Facade needs to first verify user identity: Public void deleteArticle (Identity ID, int ArticleID) throws deleteException { Article article = getArticleInfo (ArticleID); IF (Article.GetAccountId ()! = id.getaccountid ()) Throw New AuthorizationException ("Permission Denied."); Articledao.DeleteArticle (ArticleID); } To separate user authentication logic, you can use the Proxy mode, or use Spring's AOP, using the MethodInterceptor implementation, however, due to logic is simple, you can write directly in one, do not have to use too complicated design. At this point, our Blog has implemented all background business logic and provides a unified FACADE interface. The front desk web layer relies only to this FACADE interface, so that the web layer and the background coupling are very loose, even if the entire Web layer is also very easy. Nine, web layer design: use MVC mode For complex Web layers, it is essential to use MVC mode. Although Spring can easily integrate Struts, WebWorks and other web frames, Spring itself provides a very good web framework that fully implements MVC mode. Spring uses a DispatCherServlet, all specific requests are forwarded to the dispatcherservlet, then by the corresponding controller process, Controller returns a ModelandView object (because the Java language method call can only return a result, but does not support the REF parameters, so Model and The View object is returned together), Model is a Java object, usually MAP, view is the logical name of the view, usually the JSP file name, but you can also use Velocity as a view. The returned View gets a real file name through ViewResolver. First configure the Spring's MVC, declare the DispatcherServlet in Web.xml, process all requests that end up. C: servlet> servlet-maping> web-app> Spring finds a file called Dispatcher-Servlet.xml under Web-INF, we need to create this file: XML Version = "1.0" encoding = "UTF-8"?>
" http://www.springframework.org/dtd/spring-beans.dtd ">> beans> All Java Bean components for use are declared and configured in this file. The following is a bean that configures URL mapping: Class = "org.springframework.web.servlet.handler.simpleurlhandlermapping"> prOPS> Property> bean> Any request to match /article.c will be dealt with the bean of ArticleController, which also needs to declare this ArticleController: bean> ViewArticleController handles request, then generate model, and select a View: Public Class ViewArticleController IMPLEments Controller { PRIVATE FACADE FACADE; Public void setfacade (facade facade) {this.facade = facade;} Public ModelandView Handlerequest (httpservletRequest Request, HttpservletResponse response) throws exception { // Get parameters: Int articleId = integer.parseint (Request.getParameter ("ArticleID")); // Use the FACADE processing request: Article article = facade.getArticle (articleid); / / Generate Model: Map map = new hashmap (); Map.put ("Article", Article); / / Return to Model and View Name "Skin / BlueskySIMple / Article": Return New ModelandView ("SKIN / BlueskysImple / Article", MAP); } } Finally, the SKIN / Bluesky / Article view will display the results to the user. We noticed that ViewArticleController does not find or create FACADE, but is set by the container through the setFacade method, which is called IOC (Inversion of Control) or Dependency Injection. The container completes the initialization of all components through the configuration file: Property> bean> The above configuration file implementation is roughly: Facade facade = new facade (); ViewArticleController Article Article = New ViewArticleController (); ArticleController.SetFacade (FACADE); But we don't have to write or write the above code, just install our components in the XML file. All components are managed by Spring, and the default creation mode is Singleton to ensure that only one instance is only one instance. In addition, all custom anomalies are RuntimeException, and Spring provides AOP to make us very convenient to achieve exception handling. Define ExceptionHandler in the web layer, processes all exceptions and display the error message to the user in a unified ERROR page, so you only need to throw an exception in your code, you don't have to handle an exception in Controller: Using Velocity as View, using Velocity as the maximum advantage of VIEW is concise, and can output Java variables directly in HTML through simple and clear syntax. Velocity itself is a template engine that renders the model output HTML is very simple, such as displaying the username: $ {account.username} b> Change to JSP has to be written: <% = account.getuserName ()%> b> <% ...%> Mark cannot see its meaning directly in DreamWaver. If you need to embed JSP in the label: bean> bean> Although the standard Velocity template is named .s, all of our Velocity templates page are in .html as an extension, not only use DreamWaver editing very convenient, and can even use IE to watch the page effect. Ten, realize SKIN Many BLOG systems allow users to choose their favorite interface style. To achieve SKIN function is very simple, write a Velocity template for each skin, store it in a web / s / directory, then return to the corresponding view in Controller: String ViewPath = Skin.getskin (an Account.getskinid ()) "ViewArticle"; Due to the use of MVC mode, we have defined MODEL for each page, add a new SKIN very easy, just write a few must-have .html files, you can refer to existing Skin. The role of SkinManager is to automatically search for all subdirectories in / Skin / Directory at startup and manage these SKIN, map the SKINID set to the corresponding directory. There is only one Skin, you can use the visual HTML editor such as Dreamwaver to transform this Skin. 11. Additional features: Implement image upload Users must be able to upload images and therefore require file uploading. Comparative File upload assembly has commons fileupload http://jakarta.apache.org/commons/fileupload/a>) and COS fileupload http://www.servlets.com/cos), Spring has been fully integrated with these two components, here we choose Commons FileUpload. Since POST is sent to the server with a Multipart / Form-Data request to the server, you must tell the DispatCherServlet how to deal with MultipArtRequest. First declare a MultipartResolver in dispatch-servlet.xml: Class = "Org.SpringFramework.web.multipart.commons.commonsmonsmultipartResolver"> Property> bean> This once a request is a MultipArtRequest, which will be processed by MultipartResolver and then forward the corresponding controller. In UploadImageController, you can easily get file names and file contents to MultipartHtpServletRequest to MultipartHtpServletRequest: Public ModlandView HandleRequest (httpservletRequest Request, httpservletResponse response) throws exception { // Transformation is MultipartHttpRequest: MultipartHtpServletRequest MultipartRequest = (MultipartHtpServletRequest) Request; // Get files: MultipartFile File = MultipartRequest.getfile ("file"); // Get the file name: String filename = file.getoriginalFileName (); // Get the input stream: InputStream INPUT = file.getinputstream (); // Write file ... } 12. Generate thumbnails When the user uploads pictures, a thumbnail must be generated so that users can quickly browse. We don't need to use third-party software, the JDK standard library contains image processing API. We scaled a picture to 120x120 size, the following is the key code: public static void createpreviewImage (String srcfile, string destfile) { Try { FILE FI = New File (srcfile); // SRC File fo = new file (destfile); // DEST BufferedImage Bis = Imageio.Read (Fi); INT w = bis.getwidth (); INT H = BIS.GETHEIGHT (); Double scale = (double) W / h; INT NW = image_size; // final int image_size = 120; INT NH = (NW * h) / w; IF (NH> image_size) { NH = image_size; NW = (NH * w) / h; } Double SX = (double) NW / W; Double Sy = (Double) NH / H; Transform.Settoscale (SX, SY); AffineTransformop ATO = New AffineTransformoP (Transform, NULL); BufferedImage Bid = New BufferedImage (NW, NH, BufferedImage.Type_3byTe_BGR); ATO.FILTER (BIS, BID); Imageio.write (BID, "JPEG", FO); } catch (exception e) { E.PrintStackTrace (); Throw New RuntimeException ("Failed in Create Preview Image); Error:" E.GetMessage ()); } } Thirteen, realize RSS RSS is a standard XML file, and the RSS reader can read this XML file to get information about the article, the user can read Blog by the RSS reader instead of the browser, we only have to generate this XML file. Rsslibj is a small and practical Java library dedicated to reading and generating RSS, with only 25K, which can be Http://sourceforge.net/projects/rsslibj/ Download two files of rsSlibj-1_0rc2.jar and it needed, and then copy to Web / Web-INF / LIB /. Using rsslibj is very simple, let's set up HTTPSERVLETRESPONSE's Header, then output XML through rsslibj: Channel Channel = New Channel (); Channel.SetDescription (Account.getDescription ()); BaseURL = Baseurl.substring (0, n); Channel.SetLink (" http://server-name/home.c? accountid = " Accountid; Channel.Settitle (Account.getTitle ()); List Articles = Facade.GetArticles (Accountid, Account.getMaxPrpage (), 1); Iterator it = Articles.ITerator (); While (it.hasnext ()) { Article article = (article) it.next (); Channel.additem (" http: //server-name/Article.c? ArticleId = " Article.GetArticleId (), Article.getsummary (), article.gettitle () ); } // Output XML: Response.setContentType (Text / XML "); PrintWriter PW = response.getwriter (); PW.Print (Channel.GetFeed ("RSS")); PW.Close (); XIV, realize the full-text search The full-text search can greatly facilitate the user to quickly find the article they hope, and add a full-text search function for Blog. However, the full-text search is not equal to the LIKE statement of SQL, because the design of the relational database is not designed for full-text search, the database index is invalid to the full-text search, retrieving Like '% a%' in a millions of records may consume A few minutes, this is unacceptable. Fortunately, we can use the free and open source pure Java LUCENE full-text search engine, Lucene can be very easy to integrate into our blog. Lucene does not provide direct files, database indexes, only providing a high-performance engine, but the interface is unexpectedly simple. We only need to care about the following simple interfaces: Document: On behalf of a record of the Lucene database, also represents a result of the search. Field: A Document contains one or more fields, similar to the fields of the relational database. IndexWriter: Used to create a new index, that is, add new searchable large segment strings to the database. Analyzer: Spread a stroke into the word, different text, corresponding to different Analyzers, such as HTMlanalyzer, Pdfanalyzer. Query: Encapsulates a query for parsing the user input. For example, "bea blog" is parsed to "Article containing BEA and Blog". Search: Search for a query, the result will be returned in Hits. Hits: Package a search result, contains the Document collection, which can be easily output. Next, we need to establish a full-text index for the Content field of the article table. First, create a new database for Lucene, please note that this database is dedicated, we can't also know its internal structure. Each database of Lucene corresponds to a directory, just specify the directory: String Indexdir = "C: / search / blog"; Indexwriter Indexwriter = New Indexwriter (Indexdir, New StandardAnalyzer (), true); Indexwriter.close (); Then add an article to let Lucene index: String Title = "Article Title" / / Read from the database String content = "Article content" / / Read from the database // Open an index: Indexwriter indexwriter = new indexwriter (Indexdir, New StandardAnalyzer (), false); // Add a new record: Document doc = new document (); Doc.Add (Field.Keyword ("Title", Title); Doc.add (Field.Text ("Content", Content); // Establish an index: Indexwriter.addDocument (DOC); // shut down: Indexwriter.close (); To search for the article is very simple, then add an article to make it index: String Title = "Article Title" // Read from Database String content = "article content" // read from the database // Open an index: Indexwriter Indexwriter = New Indexwriter (Indexdir, New StandardAnalyzer (), FALSE // Add a new record: Document doc = new document (); Doc.Add (Field.Keyword ("Title", Title); Doc.add (Field.Text ("Content", Content); // Establish an index: Indexwriter.addDocument (DOC); // shut down: Indexwriter.close (); To search for an article: Searcher Searcher = New IndexSearcher (DIR); Query Query = queryParser.Pars (Keyword, "Content", New StandardAnalyzer ()); Hits Hits = Searcher.Search (Query); IF (Hits! = null) { For (INT i = 0; i Document doc = Hits.DOC (i); System.out.println ("Found IN" Doc.get ("Title"); System.out.println (Doc.get ("Content")); } } Searcher.close (); We design a Luceneseroprher class package full-text search function, because we must lock the directory where the database is located, we set the database to / web-inf / search / under / down, make sure the user cannot access, and initialize the directory in the configuration file: Property> bean> Fifteen, send Email Blog users allow the system to send the message from the user to the registered Email address. In order to avoid using the SMTP sender server, we manually prepare a sendmail component directly, send Email to the user mailbox through the SMTP protocol. The Sendmail component simply configures the IP address of the DNS server, you can send mail to the specified Email mailbox. And, sendmail uses buffer queues and multithreading to send email in the background, and does not interrupt normal web services. Please see Sendmail.java for specific code. XVI, test The server is configured to: P4 1.4G, 512M DDR, 100M Ethernet, Windows XP Professional SP2. The test servers are WebLogic Server 8.1, Tomcat 4.1 / 5.0, Resin 2.1.1. The test database is MS SQL Server 2000 SP3. Seventeen, Chinese support The test found that Chinese cannot be displayed normally in the page. In order to support Chinese, first add the filter in Web.xml to set the input coding to GB2312: init-param> filter> filter-mapping> Then use the text tool to search all .htm, .html, .properties file, replace "ISO-8859-1" to "GB2312", now the top page has normal display, but Lucene still does not correct Chinese, because the reason is the standard STANDARDA? NALYZER can only be parsed in English, and you can download a support Chinese. Eighteen, summary Spring is indeed an excellent J2EE framework, through Spring powerful integration and configuration capabilities, we can easily design flexible multi-layer J2EE applications without complex EJB component support. Related Resource Download JDK 1.4.2 can Http://java.sun.com. Spring Framework 1.1 can Http://www.springframework.org download. Ibatis 2.0 can Http://www.ibatis.com Download. Tomcat 4.1 / 5.0, Ant 1.6 can Http://www.apache.org download. RESIN 2.1.1 Http://www.caucho.com download. WebLogic Server / Workshop 8.1 can Http://commerce.be.com Download. JUnit 3.8 can Http://www.junit.org download. MySQL 4 and its JDBC driver can be from Http://www.mysql.com Download. MS SQL Server 2000 JDBC driver can be http://www.microsoft.com/downloads/details.aspx?familyid=07287b11-0502-461a-b138-2aa54bfdc03a&displayLANG=EN download. RSSLIBJ 1.0 can be from http://sourceforge.net/projects/rsslibj/ Download. reference "Spring Reference", Rod Johnson, etc. Ibatis SQL Maps Guide. "Apache Ant 1.6.2 manual". "Lucene Getting Started". SpringFramework's JPETSTORE example is a great design, which refers to many design patterns of JPETStore.