JSP thread safety - еще одни чайниковский вопрос
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
JSP thread safety - еще одни чайниковский вопрос
Везде на своих страничках я пишу
<%@ page isThreadSafe="false"%>
и как-то грустно мне от этого становится
Получается если люди пойдут на эту страничку одновременно, то все нафиг повалится. А где можно прочитать/посмотреть примеры как обеспечить thread-safety (кроме имплементации J2EE конечно).
Cпасибо,
Сабина
<%@ page isThreadSafe="false"%>
и как-то грустно мне от этого становится
Получается если люди пойдут на эту страничку одновременно, то все нафиг повалится. А где можно прочитать/посмотреть примеры как обеспечить thread-safety (кроме имплементации J2EE конечно).
Cпасибо,
Сабина
-
- Уже с Приветом
- Posts: 16086
- Joined: 22 Apr 2003 17:57
- Location: Колыбель
-
- Уже с Приветом
- Posts: 3811
- Joined: 14 Oct 2001 09:01
Re: JSP thread safety - еще одни чайниковский вопрос
Sabina wrote:Везде на своих страничках я пишу
<%@ page isThreadSafe="false"%>
и как-то грустно мне от этого становится
Получается если люди пойдут на эту страничку одновременно, то все нафиг повалится. А где можно прочитать/посмотреть примеры как обеспечить thread-safety (кроме имплементации J2EE конечно).
А вы пишите
<%@ page isThreadSafe="TRUE"%>
авось оно и не завалится нафик. Но я хочу предупредить, что я не специалист по этим закорючкам.
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
Бродяга wrote:А что должно повалиться?:-)
Вы бы исходник вашей странички бы приложили для начала
Они у меня все так начинаются
Ну вот скажем 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> </td>
<td><input type='submit' value='Login'>
<%if(hasCookies){%>
<%=FormUtil.checkbox(REMEMBERME, rememberme)%> 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 начнут одновременно логинится?
Сабина
-
- Уже с Приветом
- Posts: 5347
- Joined: 03 Feb 1999 10:01
- Location: NJ, USA
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
KVA wrote:Sabina wrote:Ну вот скажем user-login.jsp (за длину не ругайтесь плиз, сами попросили):
OFFTOPIC ON
Ох и выглядит же этот код. HTML перемешан с Java, JavaScript. Брррр .... Как только cтили догадались в отдельный CSS файл засунуть.
OFFTOPIC OFF
Что же мне ДжаваСкрипт в 10 строк в отдельный файл выселять?
А то что Джава с HTML перемешана, а как бы вы их тут например разделили? Это не сарказм, я правда буду очень рада, если кто-нибудь код покритикует основательно.
Сабина
-
- Уже с Приветом
- Posts: 5347
- Joined: 03 Feb 1999 10:01
- Location: NJ, USA
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.
-
- Уже с Приветом
- Posts: 1564
- Joined: 27 Nov 2001 10:01
- Location: Live free or die
Разделит всегда можно если есть желание и время.
По поводу threadsafe=false так оно вроде как всегда по умолчанию false так что можно и не писать.
А что бы не бояться что все развалится не нужно использовать статических ресурсов без синхронизации. У меня не хватило терпения прсмотреть весь ваш код, извените.
Если есть код в <%! ... %> то он не тред сейф, так что с этим поосторожнее (лучше его вообще не иметь по возможности).
Если используете внешнии статические ресурсы то нужно позаботиться об их синхронизации.
Весь остальнок код идет в тело service() метода сгенерерованного сарвлета, так что все ваши переменные (инстанс вариебл) тред сейф по определению.
По поводу threadsafe=false так оно вроде как всегда по умолчанию false так что можно и не писать.
А что бы не бояться что все развалится не нужно использовать статических ресурсов без синхронизации. У меня не хватило терпения прсмотреть весь ваш код, извените.
Если есть код в <%! ... %> то он не тред сейф, так что с этим поосторожнее (лучше его вообще не иметь по возможности).
Если используете внешнии статические ресурсы то нужно позаботиться об их синхронизации.
Весь остальнок код идет в тело service() метода сгенерерованного сарвлета, так что все ваши переменные (инстанс вариебл) тред сейф по определению.
Интересный вы человек! Все у вас в порядке. Удивительно, с таким счастьем - и на свободе. (C) О.Бендер
-
- Уже с Приветом
- Posts: 1564
- Joined: 27 Nov 2001 10:01
- Location: Live free or die
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
-
- Уже с Приветом
- Posts: 13683
- Joined: 16 Jan 2001 10:01
Я думаю нужно установить неколько правил:
- 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.
- 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.
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
-
- Уже с Приветом
- Posts: 5347
- Joined: 03 Feb 1999 10:01
- Location: NJ, USA
Sabina wrote:KVA wrote:Каждая ASP страница делает несколько вызовов business COM компонентов (естественно никакого SQL в ASP нет и в помине).
По этому поводу у меня один вопрос: а можно это как-то сделать в JSP обойдясь без session и entity beans?
Сабина
Servlet-ы использовать? HTML перемешанный c логикой меня убивает просто. Это же совершенно невозможно будет поддерживать в будущем.
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
-
- Уже с Приветом
- Posts: 5347
- Joined: 03 Feb 1999 10:01
- Location: NJ, USA
-
- Уже с Приветом
- Posts: 1564
- Joined: 27 Nov 2001 10:01
- Location: Live free or die
Sabina wrote:Буду очень признательна за пример (линк?) файла где DB connection засунута внутрь класса и в JSP он только вызывается. Сабина
Так вы просто возмите ваш код и сделайте из него класс. Назовите его скажем MyPageProcessor потом в нем там метод сделайте скажем init() неи коннекшен открывайте если надо. А всякие запросы делайте в методе скажем doAction( HttpServletRequest req ) ну и так далее.
Или я чего то не понял?
Интересный вы человек! Все у вас в порядке. Удивительно, с таким счастьем - и на свободе. (C) О.Бендер
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
-
- Уже с Приветом
- Posts: 5347
- Joined: 03 Feb 1999 10:01
- Location: NJ, USA
-
- Уже с Приветом
- Posts: 7412
- Joined: 03 Apr 2004 09:35
- Location: 1st Rock From The Moon
<%@ page isThreadSafe="false"%> - это жестоко, тем более что я не вижу JSP-объявлений, вполне себе нормальная страница, весь код в service(). Нелокальные для service() объекты должны просто синхронизироваться, и все.
custom JSP tags
Just a note: устанавливать соединение с базой каждый раз (и инициализировать класс драйвера) - это тоже жестоко. Особенно делать это три раза внутри одного и того же service(). Соединения лучше брать из пула. Либо из самодельного, либо из того, который предоставляется сервером. Серверный пул обычно доступен через JNDI lookup.
Не только статических, но и на уровне класса, то есть определенных внутри JSP-объявлений <%! .
Вызывать любой объект, который не создается внутри каждого 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(), а создается один на сеанс или на всю программу.
-
- Уже с Приветом
- Posts: 1169
- Joined: 16 Jan 2003 23:23
Sabina wrote:KVA wrote:Каждая ASP страница делает несколько вызовов business COM компонентов (естественно никакого SQL в ASP нет и в помине).
По этому поводу у меня один вопрос: а можно это как-то сделать в JSP обойдясь без session и entity beans?
Сабина
используйте обычные Java beans. всю бизнесс логику надо вынести в туда, соединения с базами данных и другими внешними ресурсами тоже.
не используйте threadsafe=false, это то же самое, что single-thread model в сервлетах, огромный performance hit.
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
Спасибо вам всем огромное! Только отъехала в Блокбастер и поужинать, вернулась а тут мне уже столько всего насоветовали!
Это я тоже слышала, но на практике не довелось.
Например в веб сервисах мы в каждом сервисе для каждого метода получали resultSet из conn.prepareStatement(...).executeQuery();
И все стейтменты хранились в отдельном файле. Но там сервис сам по себе уже не клиент, а в JSP не совсем было понятно. Значит выделять в отдельный класс (бин?), пихать его в WEB-INF/lib (или еще куда там веб сервер позволяет) и там в методах вызывать те же prepаredStatements?
Не скопируйте сюда те самые заветные три (или больше?) строки, иллюстрирующие как грамотно в таком случае установить DB connection?
Вот потому я и задаю вопросы
Спасибо,
Сабина
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:И еще я обычно придерживаюсь правила: если не получается сделать код изящным и компактным - задача считается невыполненной...
Вот потому я и задаю вопросы
Спасибо,
Сабина
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
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-я, если я правильно понимаю.
Сабина
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
-
- Уже с Приветом
- Posts: 1564
- Joined: 27 Nov 2001 10:01
- Location: Live free or die
Brazen wrote:Не только статических, но и на уровне класса, то есть определенных внутри JSP-объявлений <%! .
Если вы посмотрите в сгенерированый сервлет то увидите что все что было в <%! идет в статические переменные/методы.
Интересный вы человек! Все у вас в порядке. Удивительно, с таким счастьем - и на свободе. (C) О.Бендер