JSP thread safety - еще одни чайниковский вопрос

User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

JSP thread safety - еще одни чайниковский вопрос

Post by Sabina »

Везде на своих страничках я пишу
<%@ page isThreadSafe="false"%>
и как-то грустно мне от этого становится :(

Получается если люди пойдут на эту страничку одновременно, то все нафиг повалится. А где можно прочитать/посмотреть примеры как обеспечить thread-safety (кроме имплементации J2EE конечно).

Cпасибо,
Сабина
Бродяга
Уже с Приветом
Posts: 16086
Joined: 22 Apr 2003 17:57
Location: Колыбель

Post by Бродяга »

А что должно повалиться?:-)
Вы бы исходник вашей странички бы приложили для начала
Бог создал людей разными, Линкольн дал людям свободу, а Кольт всех уравнял.
User avatar
Pink Panther
Уже с Приветом
Posts: 3811
Joined: 14 Oct 2001 09:01

Re: JSP thread safety - еще одни чайниковский вопрос

Post by Pink Panther »

Sabina wrote:Везде на своих страничках я пишу
<%@ page isThreadSafe="false"%>
и как-то грустно мне от этого становится :(

Получается если люди пойдут на эту страничку одновременно, то все нафиг повалится. А где можно прочитать/посмотреть примеры как обеспечить thread-safety (кроме имплементации J2EE конечно).


А вы пишите

<%@ page isThreadSafe="TRUE"%>

авось оно и не завалится нафик. Но я хочу предупредить, что я не специалист по этим закорючкам. :mrgreen: :mrgreen: :mrgreen:
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

[del]
Last edited by Sabina on 18 Apr 2004 01:34, edited 1 time in total.
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

Бродяга wrote:А что должно повалиться?:-)
Вы бы исходник вашей странички бы приложили для начала


Они у меня все так начинаются :mrgreen:

Ну вот скажем user-login.jsp (за длину не ругайтесь плиз, сами попросили):

Code: Select all

<%@ page isThreadSafe="false"%>
<%@ page import="java.util.Calendar"%>
<%@ page import="java.text.DateFormat"%>
<%@ page import="java.sql.*"%>
<%@ page import="com.rdb3.java.beans.*"%>
<%@ include file="constants.inc"%>
<%

    String sqlNow = SqlUtil.getNowOracle();
    String urlString = request.getRequestURI();
    System.out.println("NOW is " + sqlNow);
    System.out.println("URL is " + urlString);

    if (session.getAttribute("visitRecorded") == null)
     { 
      StringBuffer visitSql = new StringBuffer(64); 
      visitSql.append("INSERT INTO visits (clientip, url, visitedOn) VALUES ('").append(request.getRemoteAddr()).append("','").append(urlString).append("',").append(sqlNow).append(")");
      System.out.println("VISITS_UPDATE: " + visitSql.toString());

      try
       {
        Class.forName(DRIVER);
        Connection con = DriverManager.getConnection(CONN, CONNUSERID, CONNPWD);
        Statement stmt = con.createStatement();
        stmt.executeUpdate(visitSql.toString());
        session.setAttribute("visitRecorded", new Object());
        con.close();
        System.out.println("VisitRecorded is set");
        }
     catch(Exception e){e.printStackTrace();}
      }

  String action = FormUtil.getAction(LOGOFFACTION, pageContext);
 
  String defaultUsername = FormUtil.getCookie(USERNAME, pageContext);
  if (defaultUsername == null) defaultUsername = "";

  Calendar c = Calendar.getInstance();
  DateFormat df = DateFormat.getDateInstance();
  String userid = FormUtil.getValue(USERID, pageContext);
  String username = FormUtil.getValue(USERNAME, defaultUsername, pageContext);
  String password = FormUtil.getValue(PASSWORD, "", VALIDATEACTION, pageContext);
  String email = FormUtil.getValue(EMAIL, pageContext);
  String superuser = FormUtil.getValue(SUPERUSER, pageContext);
  boolean rememberme = FormUtil.getValue(REMEMBERME, FormUtil.hasCookie(USERNAME, pageContext), VALIDATEACTION, pageContext);

  int display = 0;
  int stat = 0;
  String errorMsg = null;

  if (action == null)
      display = 0; 
    else if (action.equals(FINDACTION))
    {
     try
      {
      Class.forName(DRIVER);
      Connection con = DriverManager.getConnection(CONN, CONNUSERID, CONNPWD);
      Statement stmt = con.createStatement();
      String sql = "SELECT email, password FROM LoginInfo WHERE username='" + SqlUtil.apo(username) + "'";
      ResultSet rs = stmt.executeQuery(sql);

      if (rs.next()) // found the email address
      {
        email = rs.getString(1);
        password = rs.getString(2);

        com.rdb3.java.beans.Smtp e = new com.rdb3.java.beans.Smtp();
        e.setHost("mail.xxxxxxx.com");
        e.setUid("user%yyyyy.com");   
        e.setPwd("user");   
        e.setName("My Real Cool Website");
        e.setFrom("user@zzz.edu");   
        e.setTo(email); // the email address
        e.setSubject("Account Information");   
        e.setMessage("Here is your account password: " + password);
        e.send();
        errorMsg = "Your password has been sent -- you should be receiving it shortly in your email...";
      }
      else
        errorMsg = "Sorry, there is no account for the username of '" + username + "' in the system.";
      con.close();

    }
    catch(Exception e){errorMsg = e.toString();}
  }
  else if (action.equals(VALIDATEACTION))
  {
    String superuserusername = application.getInitParameter(SUPERUSERNAME);
    String superuserpassword = application.getInitParameter(SUPERUSERPASSWORD);
 
    if (username.equals(superuserusername) && password.equals(superuserpassword))
    {
      System.out.println("I am SUPERUSER!");
      session.setAttribute(SUPERUSER, superuserusername);
      session.setAttribute(USERNAME, superuserusername);
      System.out.println("SUPERUSER is set as a session attribute");

      if (!FormUtil.redirectToReferer(pageContext))
      FormUtil.redirect("admin-directory.html", pageContext);
      return;
    }
    else // lookup in database
    {
      try
      {

      Class.forName(DRIVER);
      Connection con = DriverManager.getConnection(CONN, CONNUSERID, CONNPWD);
      Statement stmt = con.createStatement();
      String sqlstatus = "SELECT statusCode FROM LoginInfo WHERE username='" +
                SqlUtil.apo(username.toLowerCase()) + "'";
      ResultSet rsstatus = stmt.executeQuery(sqlstatus);
      if (rsstatus.next())
        stat = rsstatus.getInt(1);

      String sql = "SELECT userid, email FROM LoginInfo WHERE username='" +
                SqlUtil.apo(username.toLowerCase()) + "' AND password='" + SqlUtil.apo(password) + "'";
      ResultSet rs = stmt.executeQuery(sql);
      System.out.println("statement=" + sql);
   
        if (rs.next())
        {
         if (stat==1)
          {
          // record the visit
          session.setAttribute(USERID, userid = rs.getString(1));
          session.setAttribute(EMAIL, email = rs.getString(2));
          sql = "UPDATE LoginInfo SET nvisits=nvisits+1,lastvisit="
            + SqlUtil.getNowOracle() + " WHERE userid=" + userid;
          System.out.println("UPDATE statement from Login:"+ sql);
          stmt.executeUpdate(sql);
          con.close();

          // save login id in a cookie
          FormUtil.setCookie(USERNAME, username, pageContext);

          if (!FormUtil.redirectToReferer(pageContext))
          {
            try
             {
              String referer = (String)session.getAttribute(REFERER);
                if (referer != null)
                  {
                   response.sendRedirect(referer);
                  }
             }
             catch(Exception e){System.out.println("Exception happend while redirecting to referer");}
 
            FormUtil.redirect("user-home.html", pageContext);
           }       
          return;
          }
          else
         {
             errorMsg = "Your user account status is not active. Please contact technical support.";
//            FormUtil.redirect("user-login.html?action=LOGOFFACTION", pageContext);
             }
          }
         else
             errorMsg = "Invalid login -- please try again...";
        con.close();
       }
       catch(Exception e){errorMsg = e.toString();}
      }
     }

  else if (action.equals(REMINDACTION))
    display = 1;
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
 <head>
  <title>
   CMSUser Login
  </title>
  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<script type="text/javascript">
<!--
 function handleError(){return true;}
 window.onerror = handleError;

 function validate() // client-side validation
 {
    if (document.login.<%=USERNAME%>.value.length == 0)
    {
      alert("Please enter your username")
      window.document.login.<%=USERNAME%>.focus()
    }
    else if (document.login.<%=PASSWORD%>.value.length == 0)
    {
      alert("Please enter your password")
      window.document.login.<%=PASSWORD%>.focus()
    }
    else // okay
      return true;   
   return false;
 }
//-->
</script>
<link rel='stylesheet' href='CMSimple.css'>
 </head>
 <body onLoad='window.document.login.<%=PASSWORD%>.focus()' leftMargin=0 background=background.gif topMargin=10>
 <%@ include file="top.inc"%>

            <br><img src="user_login.jpg" width="202" height="23" border="0"><br><br>
      <%
        if (errorMsg != null)
        {
          out.print("<center><span class='msg'>");
          out.print(errorMsg);
          out.print("</span></center><hr>");
        }
      %>
      <%
        if (display == 0)
        {
          boolean hasCookies = (request.getCookies() != null);
      %>   
       <center> 
       <form name='login' action='<%=response.encodeURL(request.getRequestURI())%>' method='post' onsubmit='return validate()'>
               <input type='hidden' name='<%=FormUtil.ACTION%>' value='<%=VALIDATEACTION%>'>
        <center>
             <table border='0'>
         <tr>
          <td nowrap>Login Name:</td>
          <td><input type='text' name='<%=USERNAME%>' value='<%=username%>' size='30'></td>
         </tr>
         <tr>
          <td nowrap>Password:</td>
          <td><input type='password' name=<%=PASSWORD%> size='30'></td>
         </tr>
         <tr>
          <td>&nbsp;</td>
          <td><input type='submit' value='Login'>
           <%if(hasCookies){%>&nbsp;
           <%=FormUtil.checkbox(REMEMBERME, rememberme)%>&nbsp;check to save ID<%}%>
          </td>
         </tr>
        </table>
               </form></center>

        <%@ include file="bot_menu.inc"%>

    <%
      }
      else if (display == 1)
      {
    %>
      Enter your username in the box below, and click on "Mail". The system will send your password to your email address. 
      <center>
       <form name='login' action='<%=response.encodeURL(request.getRequestURI())%>' method='post'>
        <input type='hidden' name='<%=FormUtil.ACTION%>' value='<%=FINDACTION%>'>
        <table border='0'>
         <tr>
          <td nowrap>Login Name:</td>
          <td><input type='text'" name='<%=USERNAME%>' value='<%=username%>' size='30'></td>
         </tr>
         <tr>
          <td colspan='2' align='center' nowrap><input type='submit' value='Mail'></td>
         </tr>
        </table>
       </form>
      </center>
      <%@ include file="bot_menu.inc"%>
    <%
      }
    %>
 <%@ include file="bottom.inc"%>

</body>
</html>


Что будет, если человек 20 начнут одновременно логинится?

Сабина
User avatar
KVA
Уже с Приветом
Posts: 5347
Joined: 03 Feb 1999 10:01
Location: NJ, USA

Post by KVA »

Sabina wrote:Ну вот скажем user-login.jsp (за длину не ругайтесь плиз, сами попросили):


OFFTOPIC ON

Ох и выглядит же этот код. HTML перемешан с Java, JavaScript. Брррр .... Как только cтили догадались в отдельный CSS файл засунуть.

OFFTOPIC OFF
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

KVA wrote:
Sabina wrote:Ну вот скажем user-login.jsp (за длину не ругайтесь плиз, сами попросили):


OFFTOPIC ON

Ох и выглядит же этот код. HTML перемешан с Java, JavaScript. Брррр .... Как только cтили догадались в отдельный CSS файл засунуть.

OFFTOPIC OFF


Что же мне ДжаваСкрипт в 10 строк в отдельный файл выселять?
А то что Джава с HTML перемешана, а как бы вы их тут например разделили? Это не сарказм, я правда буду очень рада, если кто-нибудь код покритикует основательно.

Сабина
User avatar
KVA
Уже с Приветом
Posts: 5347
Joined: 03 Feb 1999 10:01
Location: NJ, USA

Post by KVA »

Sabina wrote:Что же мне ДжаваСкрипт в 10 строк в отдельный файл выселять?
А то что Джава с HTML перемешана, а как бы вы их тут например разделили?


Может и выделить. Будет четко видно что к клиентской части, что к серверной относится. Если только производительность сильно не жмет.

Я не эксперт в построении web-applications, но мне нравится как наше приложение сделано.

1. Каждая ASP страница делает несколько вызовов business COM компонентов (естественно никакого SQL в ASP нет и в помине). Каждый вызов возвращает XML который собирается в один большой XML.
2. Затем на этот XML натравливается XSLT и генерируется HTML, который отдается клиенту.
3. В этом HTML-е обычно нет JavaScript. Точнее он есть, но как отдельный файл.

В итоге для каждой страницы имеем по одному ASP, XSLT, JS файлу. Логика в ASP и COM компонентах. За HTML отвечает отдельный XSLT файл. Клиентские скрипты отдельно в JS.

Честно говоря совершенно не представляю наше приложение (500 ASP + 500 XSLT + 300 JS + 500 stored procs + 70/80 COM компонентов) написанным в вышеприведенном JSP стиле.
Last edited by KVA on 18 Apr 2004 02:44, edited 1 time in total.
User avatar
OBender
Уже с Приветом
Posts: 1564
Joined: 27 Nov 2001 10:01
Location: Live free or die

Post by OBender »

Разделит всегда можно :) если есть желание и время.
По поводу threadsafe=false так оно вроде как всегда по умолчанию false так что можно и не писать.
А что бы не бояться что все развалится не нужно использовать статических ресурсов без синхронизации. У меня не хватило терпения прсмотреть весь ваш код, извените.
Если есть код в <%! ... %> то он не тред сейф, так что с этим поосторожнее (лучше его вообще не иметь по возможности).
Если используете внешнии статические ресурсы то нужно позаботиться об их синхронизации.
Весь остальнок код идет в тело service() метода сгенерерованного сарвлета, так что все ваши переменные (инстанс вариебл) тред сейф по определению.
Интересный вы человек! Все у вас в порядке. Удивительно, с таким счастьем - и на свободе. (C) О.Бендер
User avatar
OBender
Уже с Приветом
Posts: 1564
Joined: 27 Nov 2001 10:01
Location: Live free or die

Post by OBender »

А вообще то ту страничку таки стоит переписать так что бы вся джава была в каком нибудь классе, а JSP были бы только вызовы к нему.
Интересный вы человек! Все у вас в порядке. Удивительно, с таким счастьем - и на свободе. (C) О.Бендер
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

KVA wrote:Каждая ASP страница делает несколько вызовов business COM компонентов (естественно никакого SQL в ASP нет и в помине).


По этому поводу у меня один вопрос: а можно это как-то сделать в JSP обойдясь без session и entity beans?

Сабина
Palych
Уже с Приветом
Posts: 13684
Joined: 16 Jan 2001 10:01

Post by Palych »

Я думаю нужно установить неколько правил:
- NEVER set up connections inside of JSP;
- NEVER use DriverManager (and Class.forName()) to get connection;
- Never close connections and other "closeable" resources inside of try{} block;
...

И еще я обычно придерживаюсь правила: если не получается сделать код изящным и компактным - задача считается невыполненной...

Иметь обозримый код особенно важно для multithread programming.
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

OBender wrote:А вообще то ту страничку таки стоит переписать так что бы вся джава была в каком нибудь классе, а JSP были бы только вызовы к нему.


Буду очень признательна за пример (линк?) файла где DB connection засунута внутрь класса и в JSP он только вызывается.

Сабина
User avatar
KVA
Уже с Приветом
Posts: 5347
Joined: 03 Feb 1999 10:01
Location: NJ, USA

Post by KVA »

Sabina wrote:
KVA wrote:Каждая ASP страница делает несколько вызовов business COM компонентов (естественно никакого SQL в ASP нет и в помине).


По этому поводу у меня один вопрос: а можно это как-то сделать в JSP обойдясь без session и entity beans?

Сабина


Servlet-ы использовать? HTML перемешанный c логикой меня убивает просто. Это же совершенно невозможно будет поддерживать в будущем.
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

KVA wrote:Servlet-ы использовать? HTML перемешанный c логикой меня убивает просто. Это же совершенно невозможно будет поддерживать в будущем.


Ах вот вы о чем...Теперь понятно.

Сабина
User avatar
KVA
Уже с Приветом
Posts: 5347
Joined: 03 Feb 1999 10:01
Location: NJ, USA

Post by KVA »

Sabina wrote:Ах вот вы о чем...Теперь понятно.


А вы о чем, если не секрет? :D
User avatar
OBender
Уже с Приветом
Posts: 1564
Joined: 27 Nov 2001 10:01
Location: Live free or die

Post by OBender »

Sabina wrote:Буду очень признательна за пример (линк?) файла где DB connection засунута внутрь класса и в JSP он только вызывается. Сабина


Так вы просто возмите ваш код и сделайте из него класс. Назовите его скажем MyPageProcessor потом в нем там метод сделайте скажем init() неи коннекшен открывайте если надо. А всякие запросы делайте в методе скажем doAction( HttpServletRequest req ) ну и так далее.
Или я чего то не понял?
Интересный вы человек! Все у вас в порядке. Удивительно, с таким счастьем - и на свободе. (C) О.Бендер
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

KVA wrote:
Sabina wrote:Ах вот вы о чем...Теперь понятно.


А вы о чем, если не секрет? :D


То есть кроме сервлета по-другому никак?

Сабина
User avatar
KVA
Уже с Приветом
Posts: 5347
Joined: 03 Feb 1999 10:01
Location: NJ, USA

Post by KVA »

Sabina wrote:То есть кроме сервлета по-другому никак?


JSP, servlet, beans .... На счет четвертого не уверен. А что есть какие-то предубеждения против servlet-ов?
User avatar
Brazen
Уже с Приветом
Posts: 7412
Joined: 03 Apr 2004 09:35
Location: 1st Rock From The Moon

Post by Brazen »

<%@ page isThreadSafe="false"%> - это жестоко, тем более что я не вижу JSP-объявлений, вполне себе нормальная страница, весь код в service(). Нелокальные для service() объекты должны просто синхронизироваться, и все.
Sabina wrote:А то что Джава с HTML перемешана, а как бы вы их тут например разделили?

custom JSP tags
Sabina wrote:Это не сарказм, я правда буду очень рада, если кто-нибудь код покритикует основательно.

Just a note: устанавливать соединение с базой каждый раз (и инициализировать класс драйвера) - это тоже жестоко. Особенно делать это три раза внутри одного и того же service(). Соединения лучше брать из пула. Либо из самодельного, либо из того, который предоставляется сервером. Серверный пул обычно доступен через JNDI lookup.
OBender wrote:А что бы не бояться что все развалится не нужно использовать статических ресурсов без синхронизации.

Не только статических, но и на уровне класса, то есть определенных внутри JSP-объявлений <%! .
Sabina wrote:
KVA wrote:Каждая ASP страница делает несколько вызовов business COM компонентов (естественно никакого SQL в ASP нет и в помине).

По этому поводу у меня один вопрос: а можно это как-то сделать в JSP обойдясь без session и entity beans?

Вызывать любой объект, который не создается внутри каждого service(), а создается один на сеанс или на всю программу.
User avatar
hooch
Уже с Приветом
Posts: 1169
Joined: 16 Jan 2003 23:23

Post by hooch »

Sabina wrote:
KVA wrote:Каждая ASP страница делает несколько вызовов business COM компонентов (естественно никакого SQL в ASP нет и в помине).


По этому поводу у меня один вопрос: а можно это как-то сделать в JSP обойдясь без session и entity beans?

Сабина

используйте обычные Java beans. всю бизнесс логику надо вынести в туда, соединения с базами данных и другими внешними ресурсами тоже.
не используйте threadsafe=false, это то же самое, что single-thread model в сервлетах, огромный performance hit.
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

Спасибо вам всем огромное! Только отъехала в Блокбастер и поужинать, вернулась а тут мне уже столько всего насоветовали!

Palych wrote: - NEVER set up connections inside of JSP;


Это я тоже слышала, но на практике не довелось.

Например в веб сервисах мы в каждом сервисе для каждого метода получали resultSet из conn.prepareStatement(...).executeQuery();
И все стейтменты хранились в отдельном файле. Но там сервис сам по себе уже не клиент, а в JSP не совсем было понятно. Значит выделять в отдельный класс (бин?), пихать его в WEB-INF/lib (или еще куда там веб сервер позволяет) и там в методах вызывать те же prepаredStatements?

Palych wrote:- NEVER use DriverManager (and Class.forName()) to get connection;


Не скопируйте сюда те самые заветные три (или больше?) строки, иллюстрирующие как грамотно в таком случае установить DB connection?

Palych wrote:И еще я обычно придерживаюсь правила: если не получается сделать код изящным и компактным - задача считается невыполненной...


Вот потому я и задаю вопросы :)

Спасибо,
Сабина
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

KVA wrote: что есть какие-то предубеждения против servlet-ов?


Оно как бы одно, но веское:

out.write("\r\n\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("<html>\r\n");
out.write("</body bgcolor='white'>\r\n");
и т.д.

За что тогда боролись? В смысле зачем JSP придумывали...
Но вы видимо говорите о сервлетах куда чисто Java код вынесен, без HTML-я, если я правильно понимаю.

Сабина
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

Brazen wrote:custom JSP tags


О еще вариант. Я бы сама и не подумала в эту сторону посмотреть.

Серверный пул обычно доступен через JNDI lookup.


Говорили мне, что без J2EE ни одно JSP приложение не обходиться...

Сабина
User avatar
OBender
Уже с Приветом
Posts: 1564
Joined: 27 Nov 2001 10:01
Location: Live free or die

Post by OBender »

Brazen wrote:Не только статических, но и на уровне класса, то есть определенных внутри JSP-объявлений <%! .

Если вы посмотрите в сгенерированый сервлет то увидите что все что было в <%! идет в статические переменные/методы.
Интересный вы человек! Все у вас в порядке. Удивительно, с таким счастьем - и на свободе. (C) О.Бендер

Return to “Вопросы и новости IT”