joi, 29 ianuarie 2015

Tehnologii WEB; Lucrarea 0. Comunicarea pe porturile seriale în java

 

Tehnologii WEB

1. Obiectivul lucrării


Obiectivul lucrării este de a prezenta tehnologiile Java pentru implementarea de aplicaţii web.

2. Tehnologia Java Servlet


Pe măsură ce Web s-a dezvolat şi a început să fi utilizat pentru oferirea de servicii, a apărut necesitatea de a construi pagini cu conţinut dinamic. Applet-urile reprezintă un exemplu de tehnologie, ce utilizează platforma clientului pentru a oferi un conţinut dinamic.

La nivelul serverelor de web, printre primele tehnologii folosite pentru creare de conţinut dinamic a fost Common Gateway Interface (CGI). Ulterior au apărut şi s-au dezvoltat tehnologii similare (PHP, ASP) care oferă metode pentru construirea de pagini ce permit interacţiunea cu utilizatorul. Tehnologia propusă de firma SUN pentru construirea de pagini cu conţinut dinamic este tehnologia Servlet. Această tehnologie este bazată pe limbajul Java, componentele de timpservlet fiind implementate în acest limbaj. Bazat pe tehnologia Servlet s-au dezvoltat ulterior tehnologii ca Java Server Pages (JSP) şi Java Server Faces (JSF) -ambele destinate construirii de pagini cu conţinut dinamic.

Un servlet reprezintă o componentă web , gestionată de un container , care generează conţinut dinamic. Servlet-urile sunt clase java, ce oferă independenţă de platformă şi sunt incărcate şi executate dinamic de către server. Servlet-urilecomunică cu clienţii pe baza paradigmei cerere – raspuns. Acest model cerere – raspuns se bazează de obicei pe protocolul Hypertext Transport Protocol (HTTP).

Containerul de servleturi este o componentă ce oferă servicii de reţea prin intermediul cărora servleturile primesc şi transmit cereri şi răspunsuri de la şi către clienţi. Containerul de servleturi înmagazinează servleturile şi este responsabil pentru gestionarea acestora. Un container poate exista în cadrul unui server web sau poate fi adăugat ulterior utilizând mecanismul de extensie al serverului.


Exemplu: Un program client (web browser) accesează un server web şi transmite o cerere HTTP (poate fi de exemplu un form completat cu datele unei persoane). Acceastă cerere este preluată de către serverul de web şi în cazul în care este destinată unui servlet, este transmisă mai departe către containerul de servleturi. Containerul determină cărui servlet îi este adresată cererea şi va invoca respecivul servlettransmiţînd-ui ca parametri două obiecte cerere (request) şiraspuns (response).

Servletul va utiliza obiectul request pentru a determina cererea făcută de clientul web. După realizarea operaţiilor necesare (de exemplu scrierea sau citirea unor date dintr-o bază de date ), servletul va tansmite către client un raspuns prin intermediul obiectului response.
             
           
In continuare este prezentată structura de bază a unui servlet.

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
 
public class UnServlet extends HttpServlet {
  public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException 
{
PrintWriter            out;
String         title = "Simple Servlet ";
     
//seteaza tipulcontinutului
response.setContentType("text/html");

// trmite rasuns catre client
out = response.getWriter();

out.println("<HTML><HEAD><TITLE>");
out.println(title);
out.println("</TITLE></HEAD><BODY>");
out.println("<H1>" + title + "</H1>");
out.println("<P>Raspuns de la  UnServlet.");
out.println("</BODY></HTML>");
out.close();
  }
}

Pentru a construi un servlet care lucrează prin protocolul HTTP trebuie extinsă clasa HttpServlet. In continuare este suprascrisă metoda doGet(…) pentru a prelucara cereri GET. De asemenea poate fi rescrisă şi metoda doPost pentru a prelucra cereri POST.

Metodele doGet() şi doPost() primesc ca argumente doua obiecte: HttpServletRequest şi HttpServletRespons. Obiectul HttpServletRequest este utilizat pentru a determina cererea pe care a făcut-o clientul (datele din FORM , header HTTP etc.). Pentru aceasta clasa pune la dispoziţie metode cum ar fi: getHeaders(), getMethod(), getPathInfo(), getParameter(), getInputStream(), getReader().

Obiectul HttpServletResposn este utizat pentru a transmite raspunsul către client. Acesta conţine metodele necesare pentru a stabili headerul, tipul de raspuns şi pentru a obţine fluxul de ieşire prin intermediul căruia răspunsul este transmis către client.Clasa pune la dispoziţie metode cum ar fi: setHeader(), setStatus(), getWritergetOutputStream() etc.

Observaţie: Metodele doGet() şi doPost() sunt apelate de către metoda service(), şi există posibilitatea să se rescrie metoda service(), prin intermediul acesteia fiind prelucrate atât cererile POST cât şi cererile GET.

Diferenta dintre metodele doGet() si doPost():
DoGetDoPost
In doGet Method the parameters are appended to the URL and sent along with header informationIn doPost, parameters are sent in separate line in the body
Maximum size of data that can be sent using doget is 240 bytesThere is no maximum size for data
Parameters are not encryptedParameters are encrypted
DoGet method generally is used to query or to get some information from the serverDopost is generally used to update or post some information to the server
DoGet is faster if we set the response content length since the same connection is used. Thus increasing the performanceDoPost is slower compared to doGet since doPost does not write the content length
DoGet should be idempotent. i.e. doget should be able to be repeated safely many timesThis method does not need to be idempotent. Operations requested through POST can have side effects for which the user can be held accountable, for example, updating stored data or buying items online.
DoGet should be safe without any side effects for which user is held responsibleThis method does not need to be either safe



Ciclul de viata a unui servlet cuprinde următoarele etape:
  1. creare şi iniţializare servlet. In momentul în care un servlet este încărcat în memorie va fi apelată metoda init(ServletConfig cfg)
  2. preluare si deservire cereri sosite de la clienti
  3. distrugere servlet si eliberare memore. In momentul în care un servlet este distrus va fi apelată metoda destroy()

In momentu în care un servlet este invocat pentru prima dată containerul de servleturi va încărca o instanţă a acestui servlet în memorie. Această instanţă va prelua şi deserv toate cererile ce sosesc de la clienţi pentru acest servlet. Existenţa unei singure instanţe a unui servlet în memorie, ce deserveste toţi clienţii prezintă avantaje prin faptul că:
    • reduce memoria ocupată de servlet-uri
    • micşorează timpul de răspuns şi resursele de procesor cerute pentru deservirea unui client de către un servlet, deoarece nu trebuie construită câte o instanţă a servletului pentru fiecare cerere a unui client
    • La iniţializare, un servlet poate iniţializa toate resursele de care are nevoie, acestea persistând pe parcursul apelurilor de la diverşi clienţi. Un exemplu este iniţializarea unei conexiuni la baza de date ce mai apoi poate fi reutilizatăpentru a deservi toţi clienţii

Pentru a demonstra persistenţa unui servlet se construieşte următorul servlet:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class SimpleCounter extends HttpServlet {

  int count = 0;

  public void doGet(HttpServletRequest reqHttpServletResponse res)
                               throws ServletExceptionIOException {
    res.setContentType("text/plain");
    PrintWriter out = res.getWriter();
    count++;
    out.println("Since loading, this servlet has been accessed " +
                count + " times.");
  }
}

Servletul va fi încărcat în memorie la prima invocare a acestuia de către un client, după care această instanţă creată va deservi toţi clienţii. Din punctul de vedere al programatorului unui servlet, fiecare client poate fi văzut ca un nou threadce apelează metodele service(), doGet() sau doPost().

Marea majoritatea a containerelor de servleturi permit comunicarea între servlet-urile încărcate în memorie, astfel încât o cerere transmisă către un servlet poate fi redirectată către un alt servlet, pagina de răspuns fiind transmisă către client de ultimul servlet din lanţ.


 

3. Compilarea şi instalarea unui servlet


Modul de instalarea a unui servlet variază în funcţie de containerul de servleturi folosit. În cadrul acestui laborator se va folosi containerul de servleturi Apache Tomcat 5.5. Deoarece în cadrul laboratorului se lucrează cu mediul de dezvoltare Eclipse, în cadrul acestuia a fost instalat plugin-ul Sysdeo Tomcat Launcher (http://www.sysdeo.com/sysdeo/eclipse/tomcatplugin). Acest plugin ofere posibilitatea de startare\restartare a serverului Tomcat direct din mediul Eclipse, de asemenea oferă wizard pentru construirea de proiect pentru aplicaţie cu Servleturi şi permite testarea servlet-urilor direct din mediul Eclipse.
 Paşii necesati pentru a construi un proiect de tip Tomcat
  1. Se creează un proiect in eclipse de tip Tomcat. Se selectează File > New > Project. Se alege opţiunea Tomcat Project.
  2. La pasul următor se setează numele proiectului ServletTest.
  3. La pasul următor se setează in campul Context Name: /ServletTest. Se lasă bifată căsuţa Can update server.xml file. Se apasă butonul Finish.  In acest moment un proiect de tip Tomcat este creat in cadrul mediului Eclipse. 
  4. Se adaugă in cadrul proiectului următoarea clasă
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class UnServlet extends HttpServlet {
  public void doGet(HttpServletRequest requestHttpServletResponse response)
      throws ServletExceptionIOException
{
PrintWriter            out;
String         title = "Simple Servlet ";
   
//seteaza tipulcontinutului
response.setContentType("text/html");

// trmite rasuns catre client
out = response.getWriter();

out.println("<HTML><HEAD><TITLE>");
out.println(title);
out.println("</TITLE></HEAD><BODY>");
out.println("<H1>" + title + "</H1>");
out.println("<P>Raspuns de la  UnServlet.");
out.println("</BODY></HTML>");
out.close();
  }
} 
  1. In directorul WEB-INF din cadrul proiectului se adaugă fişierul web.xml cu următorul conţinut
<!DOCTYPE web-app
  PUBLIC "-//Sun MicrosystemsInc.//DTD Web Application 2.3//EN"
  "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
 <servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>UnServlet</servlet-class>
  </servlet>

 <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
</web-app>
  1. Se compilează aplicaţia
  2. Click dreapta pe numele proiectului si din meniul contextual se selectează opţiunea Tomcat > Update context definition
  3. Se startează Tomcat. Din meniul Tomcat se selectează opţiunea Start Tomcat
  4. Se verifica funcţionarea servletului. Dintr-un browser se incarcă pagina http://localhost:8080/ServletTest/hello

4. Aplicaţie cu servlet-uri


In cadrul acestei secţiuni este prezentată o aplicaţie simplificată, bazată pe servleturi pentru realizarea unui magazin online. Sunt construite două servleturi, acestea fiind descrise în listingul următor. Pe lângă servleturi, aplicaţia mai conţine o bază de date şi o pagină html.

Baza de date conţine un tabel ce are campurile produs, cantitate şi pret. In cadrul acestei baze de date sunt memorate produsele ce sunt disponibile în cadrul magazinului online.

Pagina html conţine două formuri. Primul dintre ele conţine două câpuri text, şi un buton submit , şi este utilizată pentru a transmite comenzi produs – cantitate către servletul ServletChek. Cel de al doile form conţine un singur buton submit, ce este utilizat pentru a transmite către ServletBuy comanda finală.

Servlet 1

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import java.sql.*;
/**
 *
 */

public class ServletChek extends HttpServlet {
  private static final String CONTENT_TYPE = "text/html";
  private String dburl="jdbc:odbc:dbShop";
  private Connection con;
  private ArrayList cos;
  private Statement st;
  //metoda este apelata automat la prima apelare a servletului
   public void init() throws ServletException {
    try{
      Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
      con = DriverManager.getConnection(dburl);
      st = con.createStatement();
    }catch(Exception e){e.printStackTrace();}
  }

  //Procesare cereri primite de la form-uri
  public void service(HttpServletRequest requestHttpServletResponse responsethrows ServletExceptionIOException {

    HttpSession session = request.getSession(true);//.1

    ArrayList cos = (ArrayList)session.getValue(session.getId());
    if(cos==null) {
      cos = new ArrayList();
      session.setAttribute(session.getId(),cos);  //.2
    }

    String r = null;
    try{
    String prod = request.getParameter("produs");
    int cant =Integer.parseInt(request.getParameter("cantitate"));
    if(comandaProdus(prod,cant)) {
      r = "Comanda primita";
      cos.add(prod+" "+cant);
    }
    else r = "Eroare receptionare comanda";

   }catch(Exception e){r = "Eroare receptionare comanda";}

    response.setContentType(CONTENT_TYPE);
    PrintWriter out = response.getWriter();
    out.println("<html>");
    out.println("<head><title>ServletChek</title></head>");
    out.println("<body>");
    out.println("<p>"+r+"</p>");
    out.println("</body></html>");

  }

  boolean comandaProdus(String prodint cant){
    ResultSet rs=null;
    try{

    rs = st.executeQuery("SELECT CANTITATE FROM PRODUSE WHERE PRODUS = '"+prod+"'");
    //verific daca produsul exista
    if(rs.next()==false) return false;
    int exista = rs.getInt("cantitate");
    //cantiatea dorita este insuficienta
    if((exista-cant)<0) return false;
    //daca cantitatea este suficienta atunci se modifica cantitatea din baza de date
    int cantNoua=exista-cant;
    st.executeUpdate("UPDATE PRODUSE SET CANTITATE="+cantNoua+" WHERE PRODUS= '"+prod+"'");

    }catch(Exception e){e.printStackTrace();return false;}

  return true;
  }
  //Stergere resurse
  public void destroy() {
    try{
    con.close();
    con=null;
    }catch(Exception e){e.printStackTrace();}
  }
}

Servlet 2

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;

public class ServletBuy extends HttpServlet implements SingleThreadModel {
  private static final String CONTENT_TYPE = "text/html";

  //Process the HTTP Get request
  public void service(HttpServletRequest requestHttpServletResponse responsethrows ServletExceptionIOException {

    HttpSession session = request.getSession(true);

    ArrayList cos = (ArrayList)session.getAttribute(session.getId()); //.3

    response.setContentType(CONTENT_TYPE);
    PrintWriter out = response.getWriter();

    out.println("<html>");
    out.println("<head><title>ServletBuy</title></head>");
    out.println("<body>");

    if((cos==null)||(cos.size()==0)){
     out.println("<p>Cosul este gol.</p>");
    }
    else
    {
      for(int i=0;i<cos.size();i++){
        out.println("<p>Produs: "+(String)cos.get(i)+"</p>");
      }
      session.invalidate(); //.4
    }
    out.println("</body></html>");
  }
}

Primul servlet are rolul de a prelua comenzile de la clienţi. Un client poate fi : un applet, un alt program java (care comunică cu servletul utilizând protocolul HTTP sau un form din cadrul unei pagin html.

In momentul în care servletul ServletChek primeşte o cerere POST sau GET, este executată metoda service() care va prelucra respectiva cerere. O variantă alternativă este de a implementa în cadrul servletului metodele doGet() şi doPost() pentru a prelucra separat cererile GET şi POST.

In cadrul servletului ServletChek se utilizează un mecanism pentru memorarea unui set cereri pe care un client le-a făcut (session tracking). Acest mecanism poartă numele de memorarea sesiunii. Acest mecanism permite servletului să memoreze toate comenzile pe care clientul le-a făcut pe parcursul unei seiuni de cumpărături.

Pentru urmărirea comenzilor unui client prima operaţie este de obţinerea a obiectului HttpSession ( 1.) . Dacă clientul a accesat pentru prima dată servletul atunci getSession(true) creează un nou obiect sesiune şi este returnat (daca parametrul este false atunci metoda returnează null).

Obiectul sesiune oferă posibilitatea de a adăuga in cadrul acestuia perechi nume – valoare, unde nume este un String iar valoare este un obiect java (2.).

Memorarea sesiunii este un mecanism prin care servleturile din cadrul aceluiaşi container pot comunica între ele pentru un anumit client . In linia (3) se observă modul în care pe baza unui obiect sesiune, un servlet poate obţine o valoarea ataşată unui nume ( adăugarea s-a făcut în (2) ).

O sesiune poate fi invalidată automat sau manual. Invalidarea automată se realizează de către serverul de web când pentru o anumită perioadă de timp un client nu a mai făcut nici o cerere. Invalidarea manuală se realizează de cătreservlet când, de exemplu clientul a terminat sesiunea de cumpărături. Invalidarea unei sesiuni înseamnă eliminarea obiectului HttpSession şi a perechilor nume – valoare asociate cu acesta. Acest lucru se realizeaza in cadrul programului pe linia (4).

Observaţie: Mecanismul de memorare a sesiunii utilizează cookies pentru asocierea unui utilizator cu un obiect sesiune. Dacă clientul nu suportă cookies sau are dezactivat acest mecanism atunci exemplul anterior nu va mai funcţiona. In acest caz trebuie utilizat mecanismul de rescriere a URL-urilor.

Cel de al doilea servlet (ServletBuy) realizează operaţiile finale în momentul în care un client decide terminarea sesiunii de cumpărături. In cazul de faţă servletul verifică dacă clientul are produse în coş sau nu are, după care realizează invalidarea sesiunii.

Pentru verificarea aplicaţiei prezentate anterior adiţional pe lângă cele două servleturi trebuie construită baza de date ce va fi localizată pe acelaşi calculator cu servleturile şi vor trebui realizate setările necesare pentru ca servleturile sa se poată conecta la baza de date (vezi lucrarea 6).
Listingul următor prezintă pagina html necesară pe partea de client.

<html>
<head>
<title>Magazin Online</title>
</head>
<body>
<p><font face="Courier">Magazin Online</font></p>

<form method="POSTaction="servlet/ServletChek">
  <p><b>
      Produs: <input type="textname="produs" size="20">
      Cantitate:<input type="textname="cantitate" size="20">
  </b></p>
  <p><input type="submitvalue="Adauga Produs" name="B1"></p>
</form>

<form method="POSTaction="servlet/ServletBuy">
  <p><input type="submitvalue="Trimite Comanda" name="B1"></p>
</form>
</body>
</html>

5. Java Server Pages (JSP)


Tehnologia JSP are la bază tehnologia servlet-urilor şi replică într-o mare măsură tehnologia Active Server Pages (ASP) de la Microsoft.

În esenţă este vorba de posibilitatea de inserare de cod java în cadrul paginilor HTML. Codul java este inclus între tagurile speciale <% şi %> .

O pagină HTML cu secvenţe de cod JSP arată astfel:

<HTML>
<HEAD><TITLE>Hello</TITLE></HEAD>
<BODY>
<H1>
<%
if (request.getParameter("name") == null) {
   out.println("Hello World");
}
else {
  out.println("Hello, " + request.getParameter("name"));
}
%>
</H1>
</BODY></HTML>

În momentul în care paginile JSP sunt încărcate, serverul (care găzduieşte paginile JSP) va genera automat, compila, încărca şi rula servleturi speciale pentru a genera conţinutul dinamic al paginii.


Servletul generat în fundal ca urmare a realizării cererii de încărcare a paginii prezentate în listingul anterior arată astfel:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class _hello1_xjsp extends HttpServlet {

  public void service(HttpServletRequest request, HttpServletResponse response)
                                 throws ServletExceptionIOException {
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    BufferedReader in = request.getReader();

    out.println("<HTML>");
    out.println("<HEAD><TITLE>Hello</TITLE></HEAD>");
    out.println("<BODY>");
    out.println("<H1>");
    if (request.getParameter("name") == null) {
      out.println("Hello World");
    }
    else {
      out.println("Hello, " + request.getParameter("name"));
    }
    out.println("</H1>");
    out.println("</BODY></HTML>");
  }
}

Prima încărcare a paginii JSP va dura mai mult deoarece serverul va trebui să genereze, compileze şi să încarce servletul corespunzător.

Elementele unei pagini JSP


Codul JSP poate fi inserat în cadrul paginilor în trei moduri.
  1. <%= expression %> în acest caz expresia este evaluată şi rezultatul este trimis către ieşire (afişare).
  2. <% code %> acest cod este inserat în cadrul metodei service() corespunzătoare servletului ce va fi generat pe baza paginii JSP.
  3. <%! code %> astfel de declaraţii sunt folosite pentru a insera cod în cadrul servletului dar în afara oricărei metode.

Expresii JSP


După cum s-a precizat expresiile sunt evaluate, convertite în şir de caractere şi trimise către ieşire. De exemplu următoarea secvenţă va trimite către ecran timpul curent al sistemului.

Current time: <%= new java.util.Date() %>

Pentru lucrul cu expresii sunt câteva expresii care sunt predefinite şi pot fi folosite în cadrul acestora. Cele mai importante sunt următoarele:

  • request, reprezintă obiectul de tip HttpServletRequest;
  • response, reprezintă obiectul de tip HttpServletResponse;
  • session, reprezintă obiectul de tip HttpSession asociat cu obiectul request;
  • out, reprezintă un obiect de tip PrintWriter ce poate fi folosit pentru trimiterea datelor către ieşire.
Un alt exemplu:

Your hostname: <%= request.getRemoteHost() %>

Scripturi JSP


Paginile JSP permit inserarea de cod complex java prin intermediul tagurilor <% %>. Câteva exemple:

<%
String queryData = request.getQueryString();
out.println("Attached GET data: " + queryData);
%>

<% if (Math.random() < 0.5) { %>
Have a <B>nice</B> day!
<% } else { %>
Have a <B>lousy</B> day!
<% } %>

if (Math.random() < 0.5) {
  out.println("Have a <B>nice</B> day!");
} else {
  out.println("Have a <B>lousy</B> day!");
}

Folosind tagurile <% %> se poate scrie cod mixt HTML şi java.

Declaraţii JSP


Declaraţiile permit definirea de metode sau atribute ce vor fi inserate în codul servletului ce va fi generat pe baza paginii JSP ( în afara metodei service()).

Inserarea declaraţiilor se face cu tagurile <%! %>. Declaraţiile nu pot fi utilizate pentru a genera direct text către ieşire standard. Exemplu:

<%! private int accessCount = 0; %>
Accesses to page since server reboot:
<%= ++accessCount %>

Directive JSP


Există două tipuri importante de directive ce pot fi utilizate în cadrul unei aplicaţii. Directiva page permite definirea de importuri de pachete şi setarea a diferiţi parametri ai clasei servlet ce va fi generate pe baza paginii JSP. Directiva includepermite includerea de fişiere în momentul în care este generat servletul asociat paginii JSP.

Ca şi exemplu directiva include poate fi folosite pentru a include o bară de navigaţie în cadrul paginilor unui site:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>Servlet Tutorial: JavaServer Pages (JSP) 1.0</TITLE>
<META NAME="author" CONTENT="webmaster@somesite.com">
<META NAME="keywords" CONTENT="...">
<META NAME="description" CONTENT="...">
<LINK REL=STYLESHEET
      HREF="Site-Styles.css"
      TYPE="text/css">
</HEAD>

<BODY>
<%@ include file="/navbar.html" %>

<!-- Part specific to this page ... -->

</BODY>
</HTML>

Exemplu pagina JSP


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>Using JavaServer Pages</TITLE>

<META NAME="author" CONTENT="content xxx yyy zzz">
<META NAME="keywords"
      CONTENT="JSP,JavaServer Pages,servlets">
<META NAME="description"
      CONTENT="A quick example of the four main JSP tags.">
<LINK REL=STYLESHEET
      HREF="My-Style-Sheet.css"
      TYPE="text/css">
</HEAD>

<BODY BGCOLOR="#FDF5E6" TEXT="#000000" LINK="#0000EE"
      VLINK="#551A8B" ALINK="#FF0000">

<CENTER>
<TABLE BORDER=5 BGCOLOR="#EF8429">
  <TR><TH CLASS="TITLE">
      Using JavaServer Pages</TABLE>
</CENTER>
<P>

Some dynamic content created using various JSP mechanisms:
<UL>
  <LI><B>Expression.</B><BR>
      Your hostname: <%= request.getRemoteHost() %>.
  <LI><B>Scriptlet.</B><BR>
      <% out.println("Attached GET data: " +
                     request.getQueryString()); %>
  <LI><B>Declaration (plus expression).</B><BR>
      <%! private int accessCount = 0; %>
      Accesses to page since server reboot: <%= ++accessCount %>
  <LI><B>Directive (plus expression).</B><BR>
      <%@ page import = "java.util.*" %>
      Current date: <%= new Date() %>
</UL>


</BODY>
</HTML>



1. Obiectivul lucrării


Scopul acestei lucrări este de a prezenta modul de accesare a portului serial şi paralel folosind limbajul de programare Java.  

2. Noţiuni preliminare


Java 2 Standard Edition nu are suport pentru accesarea porturilor seriale. Suportul pentru accesarea porturilor seriale este oferit de SUN prin intermediul pachetului Java Communication API. Prin intermediul acestui pachet programatorul poate accesa porturile seriale şi paralele într-o manieră independentă de platformă. Pentru a putea accesa porturile seriale şi paralele pachetul Java Communication trebuie instalat ulterior.

Arhitectura librării pentru accesarea porturilor seriale şi paralele este prezentată în figura 1.

Figura 1. Structura Java Communication API.

În cadrul librăriei Communication API sunt definite două clase principale: clasa SerialPort şi clasa ParallelPort. Aceste clase moştenesc clasa CommPort (aceasta este o clasă abstractă) şi definesc cele două tipuri de porturi ce pot fi accesate prin intermediul Communication API.

Constructorii claselor SerialPort şi ParallelPort nu sunt publici, astfel încât obţinerea unei instanţe a acestor clase se realizează prin intermediul unor metode statice. Pentru a obţine lista de porturi disponibile se foloseşte metoda statică CommPortIdentifier.getPortIdentifiers(). Metoda returnează o listă de obiecte de tip CommPort ce identifică toate porturile disponibile şi care pot fi deschise. Pentru fiecare obiectele de tip CommPort se poate apela metoda open() pentru a deschide respectivul port. De asemenea obiectul poate fi convertit la tipul SerialPort sau ParallelPort în funcţie de tip de port pe care il reprezintă. Fiecare dintre clasele SerialPort şi ParallelPort conţin metodele necesare pentru a putea seta parametrii specific fiecărui tip de port.

2.1 Instrucţiuni de instalare Java Communication API

 Java 2 Standard Edition nu include pachetul Java Communication API necesar pentru lucrul cu porturi seriale. Pentru a avea acces la porturile seriale si a putea trimite şi citi date pe porturile seriale si paralele (standardele RS323 şi IEEE 1284) va trebui să descăcaţi şi să instalaţi pachetul Java Communication API.
 Versiunea curentă a acestei librării este 3.0 dar pentru această versiunea nu se oferă suport pentru sistemul de operare Windows. Pentru a putea lucra din Windows va trebui să instalaţi versiunea anterioară a acestei librării – Java Communication API 2.0.

Instrucţiuni de instalare pentru platforma Windows 
  1. Descărcaţi pachetul Java Communication API 2.0
  2. Copiaţi fişierul win32com.dll în directorul <jdk>\jre\bin
  3. Copiaţi pachetul  comm.jar în directorul <jdk>\jre\lib\ext
  4. Copiaţi fişierul javax.comm.properties în directorul <jdk>\jre\lib
 S-a notat cu <jdk> calea către directorul de instalare al J2SE (de exemplu c:\java\jdk.1.5.0).

3. Listare porturi disponibile


Aplicaţia prezentată în această secţiune exemplifică modul în care se poate utiliza librăria Communication API pentru a afişa la consolă toate porturile seriale şi paralele disponibile pe calculator. După cum s-a precizat deja librăria pune la dispoziţia programatorului metoda statică CommPortIdentifier.getPortIdentifiers() ce returnează un obiect de tip Enumeration ce conţine lista de porturi disponibile.

import javax.comm.*;
import java.io.*;
import java.util.*;

public class Main {
   
    public static void testComm(){
        System.err.println("Test port.");
        Enumeration portList = CommPortIdentifier.getPortIdentifiers();

        while(portList.hasMoreElements()){
            CommPortIdentifier cp = (CommPortIdentifier)portList.nextElement();
            System.err.println("Port name="+cp.getName());
            //System.out.println(portList.nextElement().toString());
        }
    }
   
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Main.testComm();
    }
   
}

4. Deschiderea unui port serial


Această secţiune prezintă modul în care poate fi deschis un port serial pentru a realiza operaţii de scriere şi citire.

Paşii necesari pentru a deschide un port serial sunt următorii:
  1. Obţinerea obiectului de tip ComPortIdentifier ce identifică portul dorit.
  2. Conversia obiectului la tipul CommPort şi deshiderea portului prin apelarea metodei open() din cadrul obiectului de tip ComPortIdentifier. Atenţie dacă portul este deja deschis de către o altă aplicaţie atunci metoda open() va eşua şi va returna o excepţie.
  3. Construirea obiectelor de tip flux de intrare şi flux de ieşire pentru a scrire respectiv a citi date folosind metodele getInputStream şi getOutputStream. 

package commtest;
import javax.comm.*;
import java.io.*;

public class OpenSerialPort {
  static int TIMEOUTSECONDS = 5;
  static int BAUD = 38400;
   
  public static void main(String[] args) throws Exception{
     
    DataInputStream is;
    PrintStream os;
   
    CommPortIdentifier thePort = CommPortIdentifier.getPortIdentifier("COM12");
     
    CommPort communicationPort = thePort.open("Test app", TIMEOUTSECONDS * 1000);
   
    SerialPort serialPort = (SerialPort) communicationPort;
   
    serialPort.setSerialPortParams(BAUD, SerialPort.DATABITS_8,
    SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
    serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
  
   
    try {
        is = new DataInputStream(serialPort.getInputStream());
    } catch (IOException e) {
        System.err.println("Can't open input stream: write-only");
    }
    os = new PrintStream(serialPort.getOutputStream(), true);
    System.out.println("Ready to do read / write on port "+serialPort.getName());
  }
   
}

5. Deschiderea unui port paralel


Această secţiune prezintă modul în care poate fi deschis un port serial pentru a realiza operaţii de scriere şi citire.

Deschiderea portului paralel pentru scriere şi citire se face în mod similar ca şi in cazul portului serial, doar că obiectul generic de tip CommPort va trebui convertit la tipul ParallePort pentru a avea acces la metodele specifice portului paralel. 

package commtest;
import javax.comm.*;
import java.io.*;

public class OpenParallelPort {
  static int TIMEOUTSECONDS = 5;
  static int BAUD = 38400;
   
  public static void main(String[] args) throws Exception{
     
    DataInputStream is;
    PrintStream os;
   
    CommPortIdentifier thePort = CommPortIdentifier.getPortIdentifier("LPT1");
    
    CommPort communicationPort = thePort.open("Test app", TIMEOUTSECONDS * 1000);
   
    ParallelPort paralelPort = (ParallelPort) communicationPort;
   
   
    try {
        is = new DataInputStream(serialPort.getInputStream());
    } catch (IOException e) {
        System.err.println("Can't open input stream: write-only");
    }
    os = new PrintStream(serialPort.getOutputStream(), true);
    System.out.println("Ready to do read / write on port "+serialPort.getName());
  }
   
}

6. Scrierea şi citirea de pe un port


Operaţiile de scriere şi citire a porturilor se pot face în două moduri

Pachetul Communication API permite realizarea de operaţii de scriere citire atât în mod sincron cât şi in mod asincron.

6.1 Scriere şi citire în mod sincron


Comunicarea în mod sincron presupune existenţa unui protocol de comunicaţie intre aplicaţia Java şi dispozitivul extern conectat prin portul serial sau paralel în cadrul căruia se cunoaşte exact ordinea comenzilor şi a răspunsurilor ce sunt schimbate prin intermediul portului deschis. La fiecare comandă transmisă se cunoaşte exact ce răspuns trebuie să sosească de la dispozitivul conectat. După fiecare comandă transmisă aplicaţia aşteaptă răspunsul de la dispozitivul cu care realizează comunicarea.

package commtest;
import javax.comm.*;
import java.io.*;

public class SincronCommunication {
   
    static int TIMEOUTSECONDS = 5;
    static int BAUD = 38400;
    BufferedReader is;
    PrintStream os;
    SerialPort serialPort;
   
    /** Creates a new instance of SincronCommunication */
    public SincronCommunication(String port) throws Exception{
    
    CommPortIdentifier thePort = CommPortIdentifier.getPortIdentifier(port);
    CommPort communicationPort = thePort.open("Test app", TIMEOUTSECONDS * 1000);
    serialPort = (SerialPort) communicationPort;
   
    serialPort.setSerialPortParams(BAUD, SerialPort.DATABITS_8,
    SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
    serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
    
    try {
        is = new BufferedReader(new InputStreamReader((serialPort.getInputStream())));
    } catch (IOException e) {
        System.err.println("Can't open input stream: write-only");
    }
    os = new PrintStream(serialPort.getOutputStream(), true);
    System.out.println("Ready to do read / write on port "+serialPort.getName());
    }
   
    public void send(String command){
        os.print(command);
        os.println("\r\n");
    }
   
    public boolean expect(String expected) throws IOException{
        String response = is.readLine();
        return response.indexOf(expected)>=0;
    }
   
    public void commProtocol() throws Exception{
        // Send the reset command
       
        send("MESSAGE 1");
        if(!expect("OK 1"))
            System.err.println("ERROR: Device has not sent back expected response.");
        send("MESSAGE 2" );
        if(!expect("OK 2"))
            System.err.println("ERROR: Device has not sent back expected response.");
       
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        is.close();
        os.close();
    }     
   
     public static void main(String[] args) throws Exception{
         SincronCommunication sc = new SincronCommunication("COM12");
         //start communication protocol
         sc.commProtocol();
     }
}

6.2 Scriere şi citire în mod asincron


Comunicarea în mod asincron presupune că nu se cunoaşte exact în avans ordinea în care au loc operaţiile de scriere şi de citire. În acest caz aplicaţia va trebui să poată sa fie notificată atunci când apare un eveniment pe portul deshis. Pachetul Communication API oferă posibilitatea comunicaţiei în mod asincron folosind pentru aceasta şablonul de proiectare Observer \ Observable (sau altfel spus modelul orientat pe evenimente). Communication API permite înregistrarea de obiecte de tip Observer (sau Listeners) care sunt notificate în momentul în care un eveniment apare pe portul deschis.
package commtest;
import javax.comm.*;
import java.io.*;
/**
 *
 * @author mihai
 */
public class AsincronCommunicationEvents extends Thread implements SerialPortEventListener {
    static int TIMEOUTSECONDS = 5;
    static int BAUD = 38400;
    BufferedReader is;
    PrintStream os;
    SerialPort serialPort;
    /** Creates a new instance of AsincronCommunicationEvents */
    public AsincronCommunicationEvents(String port) throws Exception {
        CommPortIdentifier thePort = CommPortIdentifier.getPortIdentifier(port);
        CommPort communicationPort = thePort.open("Test app", TIMEOUTSECONDS * 1000);
        serialPort = (SerialPort) communicationPort;

        serialPort.setSerialPortParams(BAUD, SerialPort.DATABITS_8,
        SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
        serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);

        try {
            is = new BufferedReader(new InputStreamReader((serialPort.getInputStream())));
        } catch (IOException e) {
            System.err.println("Can't open input stream: write-only");
        }
        os = new PrintStream(serialPort.getOutputStream(), true);
        System.out.println("Ready to do read / write on port "+serialPort.getName());
        serialPort.notifyOnDataAvailable(true);
        serialPort.addEventListener(this);
    }
   
  public void serialEvent(SerialPortEvent event) {
     System.err.println("New event arrived.");
        switch(event.getEventType()) {
        case SerialPortEvent.BI:
        case SerialPortEvent.OE:
        case SerialPortEvent.FE:
        case SerialPortEvent.PE:
        case SerialPortEvent.CD:
        case SerialPortEvent.CTS:
        case SerialPortEvent.DSR:
        case SerialPortEvent.RI:
        case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
            break;
        case SerialPortEvent.DATA_AVAILABLE:
            try {
                String response = is.readLine();
                System.err.println("Response received:"+response);
            } catch (IOException e) {}
            break;
        }
    }

  public void run(){
        try {
        //just wait for 20 seconds
            Thread.sleep(20000);
        } catch (InterruptedException ex) {ex.printStackTrace();}
  }
   public static void main(String[] args) throws Exception{
         AsincronCommunicationEvents sc = new AsincronCommunicationEvents("COM12");
         //start communication protocol
         sc.start();
   }  
}