Showing posts with label Servlet. Show all posts
Showing posts with label Servlet. Show all posts

به چه صورت Java Servlet رو Thread Safe کنیم

وقتی مشغول طراحی وب‌سایت با استفاده از JSP/Servlet هستیم، با متدهای ()doGet و ()doPost و ()service به وفور برخورد داریم. و بحث Thread-Safety در مورد طراحی servletها بسیار مهم و حائز اهمیته، به این دلیل که کاربران به اپلیکیشن ما از هر نقطه از دنیا و در هر لحظه امکان دسترسی دارند و احتمال یا به عبارت بهتر امکان فراخوانی «دقیقا در یک لحظه» صفحه مورد نظر بسیار زیاده و برای جلوگیری از باگ‌های ناخواسته در این زمینه می‌بایست حتما سرولت‌های ما thread safed باشن.

قبل از ادامه صحبت بد نیست که در مورد thread یه توضیحی بدم. Thread یک پروسه اجرای تکی برنامه‌س. به عبارت دیگه یک زنجیره پشت سر هم اجرای قسمتی از برنامه ماست. وقتی ما می‌گیم یه برنامه multithreadه به این معنی نیست که ۲ تا نسخه از برنامه به صورت همزمان در حال اجراس، بلکه برنامه یکبار فقط اجرا شده و چندین بار توسط threadهای مختلف قسمت‌های مخلف اون دارن اجرا می‌شن.

وقتی برنامه threadهای مختلف داشته باشه عبارت Thread Safe به این تعریفه که وقتی threadهای مختلف یک بخش مشخص برنامه رو دارن اجرا می‌کنن مقداری از حافظه رو دارن به اشتراک می‌ذارن و threadهای مختلف اون بخش مشخص حافظه رو می‌تونن بخونن، داخلش بنویسن. مثال زیر رو در نظر بگیرین:
public class NotThreadSafe() {
   private int variable = 0;

   public void doSomething() {
      System.out.println("1) variable: " + variable);

      variable += 1;

      System.out.println("2) variable: " + variable);
   }
}
شرایطی را در نظر بگیرین که Thread شماره ۱ الان تو خط در حال اجرای خط ۹ام برنامه‌س و در همین لحظه هم Thread شماره ۲ خط ۷ام برنامه رو اجرا کرده باشه. خروجی که خواهیم داشت به این ترتیب می‌شه:
1) variable: 0
1) variable: 0
2) variable: 2 <---
2) variable: 1
و خب مشخصا ما انتظار این رو نداریم. اینجاس که به این کلاس Thread Safe نیست. یعنی threadهای مختلفی که به صورت نسبتا همزمان این کلاس رو صدا می‌زنن دارن روی همدیگه تاثیر می‌ذارن.

چه جوری این مشکل رو حل کنیم؟
اولین راه حل این می‌تونه باشه که متغیرهایی که امکان خوانده و نوشتن شدن دارن رو به صورت عمومی تعریف نکنیم و فقط جایی که احتیاج به تعریف و مقداردهی داره اونارو ایجاد بکنیم. مثال بالا رو به صورت زیر تغییر می‌دیم:
public class IsThreadSafe() {
   public void doSomething() {
      private int variable = 0;

      System.out.println("1) variable: " + variable);

      variable += 1;

      System.out.println("2) variable: " + variable);
   }
}
همونطور که می‌بینین ما variable رو به جای اینکه تو scope کلاس تعریف کنیم، تو scope متد ()doSomething تعریف کردیم. بنابراین variable بین threadهای مختلف کلاس مشترک نیست. بلکه داخل متدی که تعریف شده مشترک‌ه. که محتویات این داخل هم اثر روی threadهای دیگه نمی‌ذاره.

راه حل دوم رو می‌تونین به وسیله synchronize کردن مقطعی ایجاد بکنین. یعنی اون بخشی از کد برنامه رو که احتمال می‌دین ممکنه باعث اختلال تو thread safety بشه رو داخل بلاک synchronize بذارین. به این صورت:
public class IsThreadSafe() {
   private int variable = 0;
   private String mutex = "";

   public void doSomething() {
      synchronized (mutex) {
         System.out.println("1) variable: " + variable);

         variable += 1;

         System.out.println("2) variable: " + variable);
      }
   }
}
به این ترتیب مشکل ما نیز حل می‌شه ولی یه نکته خیلی مهم وجود داره که هر بخشی که داخل بلاک synchronize قرار بگیره فقط و فقط توسط یه thread در آن واحد امکان اجرا داره. و برای یه برنامه که به صورت multithread آماده شده JVM بقیه threadها رو مجبور به صبر می‌کنه که threadی که الان داخل اون بلاک قرار داره کارش تموم شه و از اون خارج بشه و بعد thread بعدی وارد اون بخش بشه. مشخصه که این وضعیت بعضی وقتها ممکنه مطلوب ما نباشه و باعث latency می‌شه.


پیاده‌سازی فرم ورود توسط ExtJS

برای استفاده از فریموورک ExtJS جهت پیاده‌سازی رابط گرافیکی فرض می‌کنم صفحه ورودی شما به این صورت می‌باشد:
<%@ page contentType="text/html; charset=utf-8" language="java" import="java.util.*"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>ExtJS Log in Sample</title>
   <link rel="stylesheet" type="text/css" href="http://localhost:8080/coder/web/theme/css/ext-all.css" />

   <script type="text/javascript" src="http://localhost:8080/coder/web/script/ext/core/ext-base.js"></script>
   <script type="text/javascript" src="http://localhost:8080/coder/web/script/ext/core/ext-all.js"></script>
   <script type="text/javascript" src="http://localhost:8080/coder/web/script/ext/user/login/login.js"></script>
</head>

<body>
</body>
   <div id="login"></div>
</html>
مشخص می‌باشد که آدرس‌دهی‌ها با توجه به شرایط شما می‌بایست تغییر یابد.

فایل login.js به صورت زیر می‌باشد :
Ext.onReady(function(){
   Ext.QuickTips.init();

   // Generating Log In Form
   var bd = Ext.get('login');

   var login_form = new Ext.FormPanel({
      id: login_form,
      monitorValid: true,
      labelWidth: 100,
      url: 'http://localhost:8080/coder/process/user/login',
      frame: true,
      monitorResize: true,
      bodyStyle: 'padding:5px 5px 0;',
      anchor: '50%',
      defaults: {
         width: 200,
         msgTarget: 'side'
      },
      defaultType: 'textfield',

      items: [{
         labelAlign: 'right',
         fieldLabel: 'نام کاربری',
         name: 'username',
         allowBlank: false,
      }, {
         fieldLabel: 'رمز عبور',
         name: 'password',
         allowBlank:false,
         inputType: 'password'
      }],

      buttons: [{
         text: 'انصراف',
         handler: function() {
            login_form.getForm().reset();
         }
      }, {
         text: 'ورود',
         handler: function() {
            if( login_form.getForm().isValid() ) {
               login_form.getForm().submit({
                  method:'POST', 

                  success: function() {
                     var nextScreen = 'http://localhost:8080/coder/';
                     window.location = nextScreen;
                  },
                  failure: function(form, action) {
                     if( action.failureType == 'server' ) {
                        obj = Ext.util.JSON.decode(action.response.responseText);
                        Ext.Msg.alert('نام کاربری و یا رمز عبور، نادرست می باشد.', obj.errors.reason);
                     } else {
                        Ext.Msg.alert('خطا!', 'در ارتباط با سرور اشکال رخ داده است : ' + action.response.responseText);
                     }
                  }, 
                  scope: this
               });
            }
         }
      }]
   });

   login_form.render(bd);

   Ext.fly('login').show();
});
آدرسی که فرم ورود به آن صفحه Submit می‌شود، و در فایل بالا به آن اشاره شده است،
http://localhost:8080/coder/process/user/login
در web.xml داریم :
<web-app>
   ...
   <servlet>
      <servlet-name>LogInController<servlet-name>
      <servlet-class>com.blogspot.coderspulse.web.user.LoginHandler</servlet-class>
   </servlet>
   <servlet-mapping>
      <servlet-name>LogInController</servlet-name>
      <url-pattern>/process/user/login</url-pattern>
   </servlet-mapping>
   ...
</web-app>
فایل LoginHandler.java
package com.blogspot.coderspulse.web.user;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginHandler extends HttpServlet {
   public void init(ServletConfig config) throws ServletException {
      super.init(config);
   }

   public void destroy() {
   }

   protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      boolean userValidated = false;

      // Do User validating  here

      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      if ( userValidated ) {
         // If User was validated, this is the Format to tell the Ext that it is a Success
         out.println("{success:true}");
      } else {
         // If User was NOT validated, this is the Format to tell the Ext that it is a Failure, and Why
         out.println("{success:false, errors:{reason:'Login failed.Try again'}}");
      }
      out.close();
   }
}
قابل ذکر است که Response ای که شما آماده کرده‌اید، می‌بایست، دقیقا با فرمت JSON بدون وجود ] در ابتدا و [ در انتهایش باشد.

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes