Servletus labi izmantot, ja lapas ģenerēšanai vajag daudz programmēt:
JSP labi izmantot, ja vajag atdalīt prezentāciju no dinamiskas satura ģenerēšanas:
Pirmais jaunizveidotas JSP lapas pieprasījums gjenerē un kompilē atbilstošu Javas klasi - servletu. Visi turpmākie pieprasījumi izmanto šo servletu, kura gjenerētais izejas kods un kompilācijas rezultāts ir atrodami servletu konteinera darba direktorijās. Gjenerētais servleta izejas teksts reizēm palīdz atklāt kompilācijas un izpildes laika kļūdas (jo kļūdu paziņojumi uzrāda rindiņu numurus gjenerētajā servletā, nevis programmētāja rakstītajā JSP lapas tekstā).

Šablona teksts - HTMLa sagatave, kuru iekļauj JSP lapā. To mēdz veidot ar dažādiem HTML dizaina rīkiem; šablona teksts nonāk klienta lapā bez izmaiņām.
Visas lietas, kas neattiecas uz šablona tekstu ierobežo ar <%...%>. Ja HTML dokumenta rezultātā vajag dabūt <%, tad var rakstīt <\%.
JSP komentārs, kurš nenonāk klienta HTML dokumentā:
<%-- JSP komentārs --%>
HTML komentāri nonāk klienta dokumentā bez izmaiņām:
<!-- HTML komentārs -->
Uz mainīgajiem session un application var izsaukt parametra uzlikšanas un nolasīšanas metodes.
public void setAttribute(String name, Object value); public Object getAttribute(String name);
Servera laiks: <%= new java.util.Date() %> <br />
Laiks citā formātā:
<%= (new java.text.SimpleDateFormat("yyyy-MM-dd"
+ " HH:mm:ss")).format(new java.util.Date()) %>
Izteiksmi izrēķina, pārveido par stringu un ievieto HTML lapā.
Klienta pieprasījums nāk no: <%= request.getRemoteHost() %>
Vēl daži izteiksmju piemēri:
Jūsu sesijas ID: <%= session.getId() %>
Formas parametra "test" vērtība:
<%= request.getParameter("test") %>
Lai uzstādītu "test" parametru, vai nu jāveido HTML forma, vai arī jāizsauc augšminētā JSP lapa ar apmēram šādu URL:
http://localhost:8888/piemeri/piemers.jsp?test=XXX
Sarežģītākām programmēšanas konstrukcijām, kuras nevar uzrakstīt ar vienu izteiksmi, domāti t.s. skriptleti jeb sīkscenāriji.
<%
String queryData = request.getQueryString();
out.println("GET pieprasījuma strings: " + queryData);
%>
Vai arī direktīva, kura uzstāda citu MIME tipu (noklusētais ir text/html):
<% response.setContentType("text/plain"); %>
Servletu programmētājam, kurš izsauc līdzīgu komandu ir jāņem vērā, ka HTTP pieprasījuma hederi (t.sk. "Content-Type" hederis) ir jāuzstāda PIRMS tiek sūtīts lapas saturs (t.sk. pirms "out.println" komandas). Toties JSP programmētājs var paļauties uz to, ka JspWriter buferizē izvadi, tātad šādus hederus JSP lapās var uzstādīt patvaļīgās vietās.
Deklarāciju redzmības līmenis ir servleta klases ķermenis (ārpus _jspService metodes)
<%! private int accessCount = 0; %> Pieprasījumu skaits kopš servletu konteinera pārstartēšanas: <%= ++accessCount %>
(Nestatisks) mainīgais "accessCount" ir kopīgs vairākām klientu sesijām, jo tās izveido tikai vienu servleta klases instanci.
Bez īpašas deklarēšanas JSP lapās ir pieejamas šādas pakotnes:
java.lang.* javax.servlet.* javax.servlet.jsp.* javax.servlet.http.*
Ja vajadzīgas citas pakotnes, jālieto importa direktīva. Piemēram,
<%@ page import="java.util.*" %>
Ja tiek izmantotas pakotnes, kuru nav Javas standarta API, programmētājam tās jāiekopē servletu konteineram specifiskā vietā. OC4J gadījumā ir divas iespējas:
Pēc noklusēšanas JSP izvada MIME tipu "text/html" (t.i. HTML failu), lietojot ISO-8859-1 kodējumu (t.i. Rietumeiropas kodu tabulu). Citos gadījumos vajag MIME direktīvu (Content-type hedera uzstādīšana):
<%@ page contentType="text/plain" %> <%@ page contentType="text/html; charset=windows-1257" %>
Varam lietot arī skriptletus. Piemēram:
<% response.setContentType("text/plain") %>
JSP programmētājam ieteicams rūpēties par skriptletu koda pavediendrošumu pašam. Tā kā vairāki klienti vienlaikus izmanto to pašu servleta instanci, dažādi pavedieni var viens otru pārtraukt jebkurā koda vietā. Piemēram šāds kods nav pavediendrošs:
<%! private int idNum = 0; %>
<%
String userID = "userID" + idNum;
out.println("Tavs ID ir " + idNum);
idNum = idNum + 1;
%>
Viens no risinājumiem būtu iekļaut kritisko posmu synchronized blokā:
<%! private int idNum = 0; %>
<%
synchronized(this) {
String userID = "userID" + idNum;
out.println("Tavs ID ir " + idNum);
idNum = idNum + 1;
}
%>
Tas nozīmē, ka attiecīgajā servletā pavediens, kurš sācis pildīt kritisko posmu, kura marķēta ar "this", nelaidīs šajā posmā (un arī citos tāpat marķētos posmos) nevienu citu pavedienu. Objekta "this" vietā var lietot arī jebkādu citu servleta klases līmenī redzamu objektu.
Ja programmētājs nevēlas pats garantēt koda pavediendrošumu, tad jāraksta direktīva:
<%@ page isThreadSafe = "false" %>
Šajā gadījumā servlets īsteno SingleThreadModel - saliek klientus rindā un laiž tos servletā pa vienam. Šādā gadījumā servletu konteiners var izveidot vairākas servleta instances (baseinu), kuru kopīgi izmanto klienti. T.i. pazeminās ātrdarbība un parādās atšķirība starp statiskajiem un nestatiskajiem servleta mainīgajiem.
Ja nevēlamies JSP lapu klientus vārdzināt ar cepumiņiem, varam neuzstādīt sesiju:
<% page session="false" %>
Šādā gadījumā JSP iebūvēto mainīgo session nevar lietot.
<%@ page errorPage = "Relatīvs URL" %>
Uz šo lapu pāriet tad, ja JSP lapā rodas nenoķerts izņēmums. Piemēram, JSP lapa connect.jsp mēģina pieslēgties datubāzei pēc norādītajiem formas parametriem, bet nepārbauda šo parametru pareizību. Liels risks rasties kļūdai, bet to apstrādājam citā JSP lapā:
<%@ page errorPage="dbconnect_error.jsp" %>
<%
String hostname = request.getParameter("hostname");
String user = request.getParameter("user");
String password = request.getParameter("password");
String port = request.getParameter("port");
String database = request.getParameter("database");
String drivername = "org.gjt.mm.mysql.Driver";
Class.forName(drivername).newInstance();
String url = "jdbc:mysql://" + hostname + ":"
+ port + "/" + database;
Connection con = DriverManager
.getConnection(url, user, password);
session.setAttribute("connection", con);
response.sendRedirect("tables.jsp?sql="
+ URLEncoder.encode("SHOW TABLES"));
%>
Cita JSP lapa - dbconnect_error.jsp apstrādā izņēmumu, kurš nonāk tur kā iebūvēts mainīgais exception. Lai varētu izmantot šādu mainīgo un kalpot par kļūdu apstrādes lapu, vispirms ir jāuzliek īpaša "isErrorPage" direktīva:
<%@ page contentType="text/html; charset=windows-1257" %> <%@ page isErrorPage="true" %> <p>DB konektēšanās mēģinājums nosprāga ar šādu kļūdu: <%= exception %></p> <p>Kļūda radās sekojošā vietā:</p> <pre> <% exception.printStackTrace(new PrintWriter(out)); %>
Pirms servleta izejas koda ģenerācijas var lapā iekopēt koda gabalu no cita faila (t.i. notiek faila statiskā iekļaušana).
<%-- navbar.jsp labots 2002-11-22 --%> <%@ include file="/navbar.jsp" %> <hr />Šai lapai specifisks saturs...
Problēma: Ja navbar.jsp mainās, tad tos JSP, kuri viņu lieto, automātiski servletu konteiners var nepārģenerēt. Tāpēc ieteicams vienmēr faila iekļaušanas direktīvai priekšā pierakstīt pēdējo iekļaujamā faila izmaiņas datumu, un to izlabot ar rokām ikreiz, kad iekļaujamais fails mainās.
| Īsais pieraksts | XML pieraksts |
| <%= ... %> | <jsp:expression>...</jsp:expression> |
| <% ... %> | <jsp:scriptlet>...</jsp:scriptlet> |
| <%! ... %> | <jsp:declaration>...</jsp:declaration> |
| <%@ page import="java.util.*" %> | <jsp:directive.page import="java.util.*"/> |
Javas Bean komponenti jeb pupiņas (Java Beans) ir Javas klases, kuras apmierina dažas papildu prasības tā, lai tās varētu instrumentēt (t.i. apstrādāt dažādos rīkos un ietvaros).
Beans komponentu specifikāciju sk. http://java.sun.com/beans/docs/ EJB (Enterprise Java Beans) nav tas pats, kas Javas pupiņas - EJB konteiners izvirza komponentiem pavisam citas prasības nekā jebkāds Java Beans konteiners.
Java Bean komponentes pazīmes:
Java Bean komponentam drīkst nebūt atsevišķas setXxx() metodes. Šajā gadījumā atbilstošo īpašību "xxx" sauc par tikai-lasāmu īpašību (read-only property).
Ar īpašiem tagiem pupiņas var inicializēt vai uzzināt jau esošās (ar jsp:useBean) un to vērtības var uzstādīt un nolasīt (attiecīgi ar jsp:setProperty un jsp:getProperty). Piemēri šo tagu lietošanai:
<%-- vai nu izveido jaunu pupiņas "mybeans.Vehicle"
instanci ar vārdu "v", vai arī savāc un izmanto
kādu veco --%>
<jsp:useBean id="v" class="mybeans.Vehicle" />
<%-- ievērojiet apostrofus "value" atribūtam -
tas ir veids, kā iekļaut JSP izteiksmi
šī atribūta vērtības definēšanai, ja tajā ir pēdiņas --%>
<jsp:setProperty name="v" property="vards"
value='<%= request.getParameter("vards") %>'/>
<jsp:getProperty name="v" property="maxLoad"/>
Sakompilētai klasei Vehicle jābūt aplikācijas WEB-INF/classes direktorijā.
Ja vairākas JSP lapas, vai vairāki citi klienti lieto tās pašas JSP lapas, ir lietderīgi redzamības apgabalu norādīt jau pupiņu radot.
session - Pupiņa pievienojas atbilstošam HttpSession objektam - redzama visās viena klienta izsauktās JSP lapās
Piemērs:
<jsp:useBean id="counter" class="coreservlets.AccessCountBean" scope="application">
JSP ļauj lietotājam ieviest savus tegus, kuri izsauc Javas kodu ar noteiktiem parametriem, un kuriem priekšā ir noteikts prefikss. Tegu bibliotēku mehānisms var būt ērtāks, salīdzinot ar pupiņām, jo katra tega izsaukums var ņemt vērā ne vien tegā nodotos parametrus, bet arī tegu savstarpējos iekļāvumus (nesting).
Pietiekami plašas tegu bibliotēkas ļauj definēt jaunas XML-veidīgas valodas ar vēlamo semantiku. Piemērs: vadības konstrukciju pieraksts ar lietotāja definētiem tegiem drukā 20 monētas metienu simulāciju:
<csajsp:repeat reps="20">
<csajsp:if>
<csajsp:condition>
<%= Math.random() > 0.5 %>
</csajsp:condition>
<csajsp:then><b>Heads</b><br /></csajsp:then>
<csajsp:else><b>Tails</b><br /></csajsp:else>
</csajsp:if>
</csajsp:repeat>
Darbības pirms JSP lapas darbināšanas:
<%@ taglib uri="/WEB-INF/sqltaglib.tld" prefix="sql" %> <html> <head> <title>SQL vaicājuma tegi</TITLE> </head> <body> <hr /> <sql:dbOpen URL="jdbc:mysql://localhost:3306/demo" user="dzonis" password="dzonis" connId="con1"> </sql:dbOpen> <sql:dbQuery connId="con1"> select * from cilveks </sql:dbQuery> <sql:dbClose connId="con1" /> <hr /> </body> </html>
Šajā piemērā pirmais vaicājums izveido HTML tabulu ar datiem no DB tabulas "cilveks". Otrais vaicājums izveido java.sql.ResultSet objektu, kuru vēlāk izmanto, lai izveidotu izvēļu sarakstu ar "SELECT" elementu.
<%@ taglib uri="/WEB-INF/sqltaglib.tld" prefix="sql" %>
<html>
<head>
<title>SQL vaicājuma tegi</TITLE>
</head>
<body>
<hr />
<sql:dbOpen
URL="jdbc:oracle:thin:@olimpus.alise.lv:1521:itndev01"
user="javatest" password="javatest" connId="con1">
</sql:dbOpen>
<sql:dbQuery connId="con1">
select * from cilveks
</sql:dbQuery>
<sql:dbQuery connId="con1"
output="jdbc" queryId="myquery">
select id,vards from cilveks
</sql:dbQuery>
<form>
<p>Izvēlies cilvēku: <select>
<sql:dbNextRow queryId="myquery">
<% out.println("<option value='"
+ myquery.getInt(1) + "'>"
+ myquery.getString(2) + "</option>");
%>
</sql:dbNextRow>
</select></p>
</form>
<sql:dbClose connId="con1" />
<hr />
</body>
</html>
Datubāzu savienojuma atvēršana patērē daudz resursu. Tos ir vērts atkalizmantot. Savienojumu dīķis jeb baseins (connection pool) veic šādas darbības:
Pieņemam, ka gan neaizņemto, gan aizņemto konekciju glabāšanai tiek izmantoti Vector objekti. Kodu raksta konekciju baseina klases konstruktorā:
availableConnections = new Vector(initialConnections);
busyConnections = new Vector();
for (int = 0; i < initialConnections; i++) {
availableConnections.addElement(makeNewCOnnection());
}
public synchronized Connection getConnection()
throws SQLException {
if (!availableConnections.esEmpty()) {
Connection existingConnection =
(Connection)availableCOnnections.lastElement();
int lastIndex = availableConnections.size() - 1;
availableCOnnections.removeElementAt(lastIndex);
if (existingCOnnection.isClosed()) {
notifyAll();
return(getConnection());
}
else {
busyConnections.addElement(existingConnection);
return(existingConnection);
}
}
}
Ja nav neizmantotu (idle) konekciju, bet baseina limits nav sasniegts, mēģina veidot jaunas konekcijas
if ((totalCOnnections() < maxConnections) &&
!connectionPending) { // Pending - kāds jau veido konekciju
makeBackgroundConnection();
}
try {
wait(); // Baseina kods šajā pavedienā sevi aptur
}
catch(InterruptedException ie) {}
return(getConnection()); // mēģina vēlreiz
Nav efektīvi veidot konekciju sinhroni, jo tas var prasīt vairākas sekundes pie noslogota Web servera, turklāt pa šo laiku cits pavediens var atbrīvot savu konekciju. Tāpēc jaunu konekciju veido asinhroni (jeb "fonā"). No wait() pavediens pamostas gan tad, ja pats izveidojis konekciju, gan tad, ja kāds cits to atbrīvojis.
Ja nav neizmantotu konekciju un ir sasniegts baseina limits.
try {
wait();
}
catch (InterruptedException ie) {}
return (getConnection());
Savienojumi aizveras pirms drazu savākšanas, bet baseins var par to parūpēties arī pats:
public synchronized void closeAllConnections() {
closeCOnnections(availableConnections);
availableCOnnections = new Vector();
closeConnections(busyConnections);
busyConnections = new Vector();
}
Nevienu no konekcijām šai laikā nedrīkst izmantot, lai piekļūtu datubāzei.
2 metodes - init(), kura inicializē servleta klases instanci un doGet(), kura apstrādā kārtējo pieprasījumu.
Servleti, kuri nelieto konekciju baseinu rezervē un atbrīvo konekcijas doGet() kodā, t.i. katrs HTTP pieprasījuma pavediens veido citu konekciju.
Var veidot 1 konekcijas baseinu - rezervēt 1 statisku konekciju init() metodē, bet doGet() metodē pārbaudīt, vai tā nav notecējusi un vajadzības gadījumā izveidot no jauna. Programmētājam tad jāsinhnronizē atbilstošie koda gabali. 1 konekcijas baseins bieži sniedz ātrdarbības uzlabojumu, tomēr visefektīvākie parasti ir nedaudz lielāki baseini.