Application of dynamic Agent in web and JDBC development

Source: Internet
Author: User
Tags stmt throwable

Web case

There is currently a 2005, STRUTS1-based Web project A, whose validation part relies on Master SSO (Single Sign-on). When requesting site A, the user will be forced to take the SSO authentication, after authentication, the master will automatically forward the request to the a site, and in the request header to save the login user ID of the new attribute Sm_user, and a site based on the user ID to provide the corresponding service. Since the project is a saved item, there are a lot of residual test code like the following.

[Java]View PlainCopy 
    1. String user_id = Request.getheader ("Xx_user");
    2. if (user_id = = null) {
    3. user_id = "my_hard_coded_user_id";
    4. }
    5. UserProfile userprofile = new Bizdao (). GetUserProfile (USER_ID);

The root cause is that it is not possible to have SSO docking in the production environment at the time of local testing, and only after submitting the code to the public dev server, UAT server or PROD server can you enjoy the Xx_user data provided by SSO as a station, so the programmer cannot get the header data. The local environment is hard-coded directly and simply rudely. In a small team of only a few people, this kind of processing may seem to be indifferent, but the number of people who have handled it quite a lot, many are accustomed to use their own ID for testing, so in the historical version of SVN, the hard-coded ID from a to B, change to C, to D ...

Problem analysis

Everyone chooses to use their own ID or someone else's ID according to their preferences, so is there a way to unify the interface once and for all? Probably the most easy to think of is httpservletrequest.setheader, unfortunately HttpServletRequest does not have such an API, why? The personal guess may be that the request originates from the client, and the server side should maintain the original, pure, and non-toxic nature of the requests, while HttpServletResponse (http://docs.oracle.com/javaee/6/api/javax/ servlet/http/httpservletresponse.html) is under the control of the application, so programmers can arbitrarily trample on it, SetHeader, AddHeader and GetHeader, Getheaders, Getheadernames. Since there is no API available, go directly to the packaging agent, with Google's help we can find a ready-made solution to the header in the request to re-customize, focusing on the implementation of the Httpservletrequestwrapper class.

http://vangjee.wordpress.com/2009/02/25/how-to-modify-request-headers-in-a-j2ee-web-application/

In fact, this program was found after I implemented the dynamic agent scheme, the two ideas are almost the same, are the original request for the packaging agent, re-implementation of the GetHeader method.

Solution Solutions

No nonsense, just on the code!

[Java]View PlainCopy  
  1. Private static class Requestinvocationhandler implements Invocationhandler {
  2. private HttpServletRequest wrappedrequest;
  3. Public Requestinvocationhandler (HttpServletRequest r) {
  4. Wrappedrequest = R;
  5. }
  6. public static String dummydata = "my_hard_coded_id";
  7. Public object Invoke (Object proxy, Method method, object[] args) throws Throwable {
  8. if ("GetHeader". Equals (Method.getname ()) && args.length = = 1 && "Sm_user". Equals (args [0])) {
  9. return dummydata;
  10. }
  11. return Method.invoke (wrappedrequest, args);
  12. }
  13. public static HttpServletRequest Createrequestwapper (HttpServletRequest r) {
  14. if (null! = R.getparameter ("U")) {
  15. Dummydata = R.getparameter ("U");
  16. }
  17. return (HttpServletRequest) (Proxy.newproxyinstance (HttpServletRequest. Class.getclassloader (),
  18. New class[] {httpservletrequest. Class},
  19. New Requestinvocationhandler (R));
  20. }
  21. }

The above code implements the proxy for the request object, and all calls to the request object need to go through the Invoke method. In the Invoke method, we can make finer-grained control of different method signatures. For example, the problem mentioned in the Web case can be customized specifically for the GetHeader method, and there is no such data in the request header, but we can "build" the data abruptly. In addition to "build" the test data, but also can be carried in the request of the parameters "U" Dynamic Data modification, so that the user Switching function.

Now that the problem with the proxy class has been solved, here's how to implant the proxy object. When and where it can be understood as a cut-in time, In fact, the initial is to implant the proxy object in the Org.apache.struts.action.RequestProcessor, but in the use of the implementation has found that the disadvantage is that the non-struts request can not use proxy objects, such as direct access to the JSP file or other servlet The service path that is provided. At this point it is natural to think of filter, and the use of filter is a pain, adding a brand-new filter bar, a bit of damage to the overall design of the feeling, and put into other filter, it looks nondescript. But at least for testing purposes, these two options can be compromised by one of the following.

[Java]View PlainCopy  
    1. Public void DoFilter (ServletRequest request, servletresponse response, Filterchain chain) throws IOException, Servletexception
    2. Request = Requestinvocationhandler.createrequestwapper ((httpservletrequest) request);
    3. Chain.dofilter (request, response);
    4. }

The overall workflow is as follows, the user makes the request first, and then the requests object is replaced with the proxy object in the filter, and the replacement request is passed into the Dofilter method, and then either servlet, struts, or JSP. All of them use our custom request object.

[Plain]View PlainCopy 
  1. +---------+         +---------+
  2. |         |         | |
  3. Request | | +-----> | Servlet |
  4. --------> |         Filter |         | |
  5. |         +----+  |  | Struts |
  6. |         Request |         | |
  7. Response |         is |         | |
  8. lt;--------+ |         Wrapped |         | |
  9. | Here |         <-----+ | |
  10. |         |         | |
  11. +---------+         +---------+
Postscript

Of course, if you want to modify the request object more than this method, such as the Orthodox way is to define a httpservletrequestwrapper to redefine the request, the specific case please refer to HTTP// vangjee.wordpress.com/2009/02/25/how-to-modify-request-headers-in-a-j2ee-web-application/

JDBC Case Background Description

Let's take a look at the problems facing the project and the desired solution. In the project contacted by the author, the direct use of the original JDBC technology, Java.sql.PreparedStatement and Java.sql.ResultSet almost occupied the data access layer, there is no slightest or mapping signs, it seems not very sad to urge? Oh, life. In the project development to half of the time, suddenly found to support the Japanese characters, and in the past has been used in English test. Well, here's the question, how does a database encoded as 8859_1 receive UTF-8 in Java? And a lot of CRUD functionality has been completed, each implementation of the number of fields involved is quite large, is not for each field adjustment?

Problem analysis

For the scenario described above, let's start with a simple analysis. Currently facing problems can be categorized into two types, the first type is garbled, this can be resolved by recoding, the second class is faced with a large number of code implementation, if all the content is adjusted, many functions will have to re-test, high cost, but it is feasible.

In view of the problem of garbled characters, from the angle of implementation, we can encode and convert the input data and output data separately. When using PreparedStatement, encode conversions for all calls to SetString, and for data reads, the resulting string returned by resultset.getstring can be encoded. The following is an example of UTF-8 to 8859_1, which enables the conversion of the code (reverse conversion simply exchanges the location of the encoded name):

[Plain]View PlainCopy
    1. New String (New string ("abc"). GetByte ("UTF-8"), "8859_1")

If each call of preparedstatement.setstring and resultset.getstring is encoded and converted in this way, the workload is not only onerous, but also not conducive to the maintenance and porting of code. From the API, in fact, we can setstring and getstring for "HOOK", the use of proxy mode is more suitable, dynamic agent is most appropriate.

Solution Solutions

Coding problem solved, the use of dynamic Agent Unified setstring and GetString interface to self-control, so that all the calls in our controls and how worry can not unified Jiangshan!

[Java]View PlainCopy
  1. Public static ResultSet Createresultsetjpwrapper (ResultSet rs) {
  2. return (ResultSet) (Proxy.newproxyinstance (ResultSet. Class.getclassloader (),
  3. New class[] {ResultSet. Class},
  4. New Resultsetjpwrapper (RS));
  5. }
  6. Private static class Resultsetjpwrapper implements Invocationhandler {
  7. private ResultSet Wrappedresultset;
  8. Public Resultsetjpwrapper (ResultSet rs) {
  9. Wrappedresultset = RS;
  10. }
  11. Public object Invoke (Object proxy, Method method, object[] args)
  12. throws Throwable {
  13. Object result = Method.invoke (Wrappedresultset, args);
  14. if ("getString". Equals (Method.getname ()) && null! = result) {
  15. result = Commonutils.convertcharset ((String) result, isaconstants.charset_8859_1, isaconstants.charset_utf_8);
  16. }
  17. return result;
  18. }
  19. }
  20. Public static PreparedStatement Createpreparedstatementjpwrapper (PreparedStatement pstmt) {
  21. return (PreparedStatement) (Proxy.newproxyinstance (PreparedStatement. Class.getclassloader (),
  22. New class[] {PreparedStatement. Class},
  23. new Preparedstatementjpwrapper (pstmt));
  24. }
  25. Private static class Preparedstatementjpwrapper implements Invocationhandler {
  26. private PreparedStatement wrappedstatement;
  27. Public Preparedstatementjpwrapper (PreparedStatement pstmt) {
  28. Wrappedstatement = pstmt;
  29. }
  30. Public object Invoke (Object proxy, Method method, object[] args)
  31. throws Throwable {
  32. if ("setString". Equals (Method.getname ()) && args.length = = 2 && null! = args[1] && String. class.equals (args[1].getclass ())) {
  33. args[1] = Commonutils.convertcharset (String) args[1], isaconstants.charset_utf_8, isaconstants.charset_  8859_1);
  34. }
  35. return Method.invoke (wrappedstatement, args);
  36. }
  37. }

The next step is to refactor in places where you need to use PreparedStatement and resultset.

[Java]View PlainCopy
    1. Before refactoring
    2. PreparedStatement stmt = conn.preparestatement (sql);
    3. After refactoring
    4. PreparedStatement stmt = dbutils.createpreparedstatementjpwrapper (conn.preparestatement (SQL));
    5. Before refactoring
    6. ResultSet rs = Stmt.executequery ();
    7. After refactoring
    8. ResultSet rs = Dbutils.createresultsetjpwrapper (Stmt.executequery ());

After refactoring, both stmt and rs,setstring and GetString are hijacked by our defined dynamic proxies, re-encoded before or after data entry, all of which are transparent to developers and do not pollute existing logic.

Postscript

What we're talking about here is a dynamic proxy application case, and it doesn't mean that you have to use dynamic proxy as the only solution to the problem in this case. In fact, dynamic agents here is not necessarily the best implementation, if possible, it is best to re-set the database to modify the default encoding, or in the application layer of uniform coding, but these often do not apply to a more than 10 years, dozens of people, and different styles and "rotten" smelly project. In fact, the actual database varchar type stored in a variety of string encoding, Euc-jp/shiftjis/utf-8/8859_1, and these codes may exist in a table, to the life!

Application of dynamic Agent in web and JDBC development

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.