[J2EE] HSQL in-process en Servlet

Pagina: 1
Acties:

  • tweakerbee
  • Registratie: Maart 2000
  • Laatst online: 29-11 20:34
Tomcat 5.5 en HSQL in-process mode leveren bij mij wat problemen op.

Het probleem met onderstaande code is dat deze bij veel requests (ik simuleer dat even door Ctrl-R ingedrukt te houden) een "Access is denied: Session is closed" melding geeft. Google was niet erg behulpzaam, en ik denk dat ik eigenlijk ook wel weet wat het betekend. De database connectie lijkt tussen de getConnection() binnen doPost en de query waarbij hij daadwerkelijk gebruikt wordt afgesloten te worden. Draait Tomcat meerder Servlet instanties tegelijk?

Ik wil graag mijn context kunnen reloaden, maar daarvoor moet ik netjes alle connecties afsluiten, anders krijg je locking problemen (Tomcat houdt lock op database.lck, maar de oude servlet instantie is wel opgeruimd door de gc. => applicatie dood).

Iemand enig idee hoe dit op te lossen is? Liefst wil ik ook de inprocess mode blijven gebruiken.

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class AutoComplete extends HttpServlet {
    private Connection c;
    
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // contenttype, headers, querystring opvragen etc.
        
        try {
            Connection c = null;
        c = getConnection();
        Statement sqlStatement = c.createStatement();
            String sql = "SELECT term FROM table WHERE x='y%'";
            ResultSet res = sqlStatement.executeQuery(sql);
            while(res.next()) {
                //recordset verwerken
            }
        }
        catch (SQLException e) { 
       // errors catchen, hier komt die SqlException vandaan
        }
        finally {
            try {
                closeConnection();
            // errors catchen
        }
    }
    
    private void closeConnection() throws ClassNotFoundException, ServletException, SQLException {
        Connection c = getConnection();
        Statement s = c.createStatement();
        s.executeUpdate("SHUTDOWN");
        c.close();
    }

    private Connection getConnection() throws ClassNotFoundException, ServletException, SQLException {
        if(c!=null) {
            if(!c.isClosed()) return c;
        }
        Class.forName("org.hsqldb.jdbcDriver" );
        String db_path = settings.getDb();
        String db = getServletContext().getRealPath(db_path);
        c = DriverManager.getConnection("jdbc:hsqldb:file:"+db, "sa", "");
        return c;
    }

}


De niet relevante code heb ik er even uitgesloopt voor leesbaarheid.

You can't have everything. Where would you put it?


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Je deelt de connectie tussen de requests. Ipv een globale connectie, moet je per request een connectie aanmaken. Dit kun je optimaliseren door gebruik te maken van een connectionpool (dbcp is wel een makkelijke).

[edit]
Ik zie dat je het niet deelt, maar wel verwarrend dat je zowel een globale als een lokale connection variable hebt.

pseudo code:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            Connection c = connectionpool.getConnection();
           Statement sqlStatement = c.createStatement();
            String sql = "SELECT term FROM table WHERE x='y%'";
            ResultSet res = sqlStatement.executeQuery(sql);
            while(res.next()) {
                //recordset verwerken
            }
        }
        catch (SQLException e) { 
       // errors catchen, hier komt die SqlException vandaan
        }
        finally {
             connectionpool.return(c);
        }
    }

Verder is je code ook voor wat verbeteringen vatbaar: combinatie van db logica en presentation is ook maar zo zo. Ik weet niet of dit voor een huiswerk opdracht is, of dat dit echt ingezet gaat worden.

[ Voor 49% gewijzigd door Alarmnummer op 20-12-2006 12:23 ]


  • tweakerbee
  • Registratie: Maart 2000
  • Laatst online: 29-11 20:34
Dank voor de feedback. Het zal uiteindelijk wel echt ingezet moeten gaan worden, maar alleen in een kleine omgeving. (Code is op dit moment weer een zooi met al die catch clauses, maar ik was nu gewoon aan het uitzoeken waar het precies fout ging. Dat zal nog wel opgeruimd worden als ik begrijp wat er aan de hand is.)

Probleem is dat ik maar één connectie naar deze database kan maken, anders krijg je volgens mij te maken met locking issues. Een pool waarbij alle connecties naar deze database lopen is dus niet mogelijk.
De lokale variabele is er om te zorgen dat als de connectie nog niet aangemaakt is dat wordt gedaan.

Hoe zou jij in zo'n kleine applicatie dan de db code en presentatie code scheiden? Aparte class maken voor databaseconnectie/queries en de servlet echt alleen voor presentatie gebruiken, dus alle db logica dus naar die tweede class?

[ Voor 13% gewijzigd door tweakerbee op 20-12-2006 13:07 ]

You can't have everything. Where would you put it?


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

tweakerbee schreef op woensdag 20 december 2006 @ 13:06:
Dank voor de feedback. Het zal uiteindelijk wel echt ingezet moeten gaan worden, maar alleen in een kleine omgeving. (Code is op dit moment weer een zooi met al die catch clauses, maar ik was nu gewoon aan het uitzoeken waar het precies fout ging. Dat zal nog wel opgeruimd worden als ik begrijp wat er aan de hand is.)

Probleem is dat ik maar één connectie naar deze database kan maken, anders krijg je volgens mij te maken met locking issues.
? Je kunt zo veel connecties naar de db opentrekken als de db toe laat (in principe). En wat bedoel je met locking issues? Isolation problems? Daar zorgt de database transactie juist voor. Ik kan je hier dus niet helemaal volgen.
Een pool waarbij alle connecties naar deze database lopen is dus niet mogelijk.
De lokale variabele is er om te zorgen dat als de connectie nog niet aangemaakt is dat wordt gedaan.
Code is ook alles behalve threadsafe.
Hoe zou jij in zo'n kleine applicatie dan de db code en presentatie code scheiden? Aparte class maken voor databaseconnectie/queries en de servlet echt alleen voor presentatie gebruiken, dus alle db logica dus naar die tweede class?
Precies. Dan is het tenminste nog een beetje te testen (en te onderhouden).

  • tweakerbee
  • Registratie: Maart 2000
  • Laatst online: 29-11 20:34
* tweakerbee gaat het even omgooien en kijken of dat eerste wel echt niet werkt.

Ik was in de veronderstelling dat er maar één connectie per database gemaakt kon worden omdat HSQLDB 'em lockt. Zal dat nu even gaan verifieren.
Verder is die threadsafety precies mijn probleem ;). Het probleem doet zich voor wanneer er 2 threads actief zijn. Helaas heb ik nog niet fatsoenlijk (thread-safe) leren programmeren (nog tips?).

Wat gebeurt er trouwens met connectionpooling als je pool te klein is en je teveel concurrent requests krijgt?

Alvast bedankt, ik weet dat ik iedereen een beetje lastig val met vrij elementaire dingen, maar heb simpelweg geen andere plaats waar ik het vandaan kan halen.

You can't have everything. Where would you put it?


  • momania
  • Registratie: Mei 2000
  • Laatst online: 19:04

momania

iPhone 30! Bam!

Java:
7
8
9
try {
        Connection c = null;
        c = getConnection(); 

Hiermee overschrijf je altijd je globale connectie en zet deze dus op null.
getConnection() zal dan volgens mij ook altijd een nieuwe connectie terug geven.

De "Access is denied: Session is closed" melding krijg je weer omdat je ergens in je code de sessie weer sluit.
Een servlet draait als 1 instantie over meerdere threads. Elke thread deelt dus nu de class variabele 'Connection c'.

Het gebeurt dus dat je in de ene thread net de connectie sluit terwijl je hem in de andere thread net nodig hebt.

Als je echt maar 1 connectie naar de database mag maken in totaal (hoewel me dat stug lijkt) kan je alsnog gewoon een connectionpool gebruiken en daar de max op 1 zetten.

Neem je whisky mee, is het te weinig... *zucht*


  • tweakerbee
  • Registratie: Maart 2000
  • Laatst online: 29-11 20:34
Krijg dus inderdaad ook nog "Access is denied: Session is closed." wanneer ik connection pooling gebruik. Ik zal de toegang tot die ene database dus allemaal sequentieel moeten laten verlopen. Hoe kan ik dat netjes implementeren.

Een smerige manier had ik al in m'n hoofd: Bijhouden hoeveel instanties de connectie gebruiken, en als er 0 instanties zijn die de connectie gebruiken hem sluiten. Dat lijkt me overigens ook niet 100% thread safe. Zal wel iets met een mooi synchronized method moeten worden, gok ik.

* tweakerbee gaat weer verder lezen/zoeken

You can't have everything. Where would you put it?


  • momania
  • Registratie: Mei 2000
  • Laatst online: 19:04

momania

iPhone 30! Bam!

tweakerbee schreef op woensdag 20 december 2006 @ 16:52:
Krijg dus inderdaad ook nog "Access is denied: Session is closed." wanneer ik connection pooling gebruik.
Dan heb ik het vermoeden dat je je connection pool niet goed gebruikt en dus zelf nog ergens de connection sluit oid.

Laat nog eens wat code zien van wat je nu hebt dan met die connectionpool? :)

Neem je whisky mee, is het te weinig... *zucht*


  • tweakerbee
  • Registratie: Maart 2000
  • Laatst online: 29-11 20:34
momania: Je had helemaal gelijk. :) Gelukkig was ik er zelf al achter voordat ik je bericht las.

M'n connectionpool doet het nu goed. Het is vervelend dat ik een absoluut pad moet definieren naar m'n database, maar daar kom ik ook wel weer uit. Het probleem is echter - weliswaar een specifiek HSQL probleem - dat je bij het sluiten van je connectie niet de database netjes afsluit.

Gevolg is dat er een lock blijft hangen wanneer je de servlet context reload. Terug bij af dus.
Het afsluiten van een HSQLDB gaat met executeUpdate("SHUTDOWN"); Dan is de connectie echter dood en krijg je bij het gebruik ervan dus Access denied: Session is closed.

Is het maken van een synchronized method die de connectie managed en data ophaalt een idee? Het geeft wel een performance hit, maar daar ontkom ik door het gebruik van die inprocess database niet aan ben ik bang.

edit:

Het gebruik van een property aan de connectionstring, nml. ";shutdown=true" werkt ook niet. :( Schijnt alleen onder Linux te werken?!

[ Voor 28% gewijzigd door tweakerbee op 20-12-2006 17:33 . Reden: laatste alinea was een beetje vaag ]

You can't have everything. Where would you put it?


  • tweakerbee
  • Registratie: Maart 2000
  • Laatst online: 29-11 20:34
Ik heb het nu "opgelost" door een init() en een destroy() methode aan te maken voor deze specifieke servlet. Het werkt. Alleen zit ik nu weer met het probleem van scheiden van database logic en content. De init() en destroy() methodes werken namelijk alleen bij servlets. Is er voor een gewone class ook zo'n methode?

You can't have everything. Where would you put it?

Pagina: 1