Avatar billede zamiel Nybegynder
11. marts 2008 - 15:02 Der er 17 kommentarer og
1 løsning

Angående JDBC og connection pools

jeg har fået til opgave at rette nogle servlets som åbenbart ikke opfører sig som de bør. Der opstår exceptions forskellige steder men alle har de denne overskrift.

<BEA-000627> <Reached maximum capacity of pool "ConnectionPool", making "0" new resource instances instead of "1".>
<BEA-101020> <[ServletContext(id=32869231,name=*******,context-path=)] Servlet failed with Exception

Dette får mig så til at tro at database forbindelserne ikke bliver lukket korrekt. Men det ser de nu ellers ud til at bliver i kildekoden. Så er der nogen der kan fortælle hvorfor at der løbes tør for forbindelser til DB?


public class setBestilling extends HttpServlet
{
    public void init(ServletConfig config) throws ServletException
    {
        super.init(config);
    }
   
    public void service(HttpServletRequest request, HttpServletResponse response)   
    throws ServletException, IOException
    {
        Connection connection = null;
        Statement statement = null;
        Context ctx = null;
        ResultSet res = null;
        String sql="";
        String next = "/onlinebestilling.jsp";
        boolean zero = true;
   
        request.setCharacterEncoding("ISO-8859-1");
       
        request.setAttribute("sendt","false");
        request.setAttribute("zero","false");
       
        try
        {
            ctx = new InitialContext();
            Object o = ctx.lookup("defaultPool");
            DataSource ds = (DataSource)PortableRemoteObject.narrow(o, DataSource.class);
            connection = ds.getConnection();
            statement = connection.createStatement();
        }
        catch (SQLException e){System.out.println(e);}
        catch(NamingException ne){System.out.println(ne);}
       
        HttpSession session = request.getSession();
        if(session.getAttribute("user") == null) next = "/index.jsp";
        else
        {
            String bestilling = "*************"     
                                     
              bestilling+="</table></body></html>";
           
            String modtager = "<html>"+
                              "<head>"+
                              "<meta http-equiv='Content-Language' content='dk'>"+
                              "<meta http-equiv='Content-Type' content='text/html; charset=ISO-8859-1'>"+
                              "</head>"+
                              "<body>"+
                              "<br> Bestilling afgivet af : <br>"+
                              "<br> "+request.getParameter("navn")+
                              "<br> "+request.getParameter("adresse")+
                              "<br> "+request.getParameter("postnr")+
                              "<br> "+request.getParameter("by")+"<br><br>";
             
            if(!zero) request.setAttribute("sendt","true");
            else request.setAttribute("zero","true");
           
            try
            {
                sql = " SELECT FORNAVN,EFTERNAVN,ADRESSE,POSTNR,BYNAVN"+
                            " FROM BRUGERE"+
                            " WHERE BRUGERNAVN LIKE '"+session.getAttribute("user")+"'";
                res = statement.executeQuery(sql); res.next();
               
                request.setAttribute("fornavn",res.getString("fornavn"));
                request.setAttribute("efternavn",res.getString("efternavn"));
                request.setAttribute("adresse",res.getString("adresse"));
                request.setAttribute("postnr",res.getString("postnr"));
                request.setAttribute("bynavn",res.getString("bynavn"));
            }
            catch(SQLException se)
            {
                System.out.println(se);
                next = "/error.jsp";
            }
            finally
            {
                try
                {
                    statement.close();
                    connection.close();
                    ctx.close();
                }
                catch (SQLException e){System.out.println(e);}
                catch(NamingException ne){System.out.println(ne);}
            }
        }
       
        RequestDispatcher rd;
        rd = getServletContext().getRequestDispatcher(next);
        rd.forward(request,response);
    }
   
    public void sendMail (String to, String from, String subject, String content)
    {
        try
        {
            Authenticator  authenticator = null;
            Properties  properties = new Properties ();
            properties.put ("mail.smtp.host","localhost");
            javax.mail.internet.MimeMessage  msg = new javax.mail.internet.MimeMessage (
            javax.mail.Session.getInstance ( properties, authenticator) );
           
            msg.setRecipients (Message.RecipientType.TO,to);
            msg.setFrom (new InternetAddress(from));
            msg.setSubject(subject,"ISO-8859-1");
            msg.setHeader("Content-Language","dk");
            msg.setContent(content,"text/html; charset=ISO-8859-1");
            Transport.send ( msg );
        }
        catch (Exception ex){System.out.println(ex);}
    }
   
    public void doGet(HttpServletRequest request, HttpServletResponse response)   
    throws ServletException, IOException
    {
        doPost(request,response);
    }
   
    public void destroy(){}
}


////////////////////////

public class RetrieveOrders extends HttpServlet
{
    Connection connection = null;
    Statement statement = null;
    Context ctx = null;
   
    public RetrieveOrders()
    {
       
    }

    public void init(ServletConfig config)
        throws ServletException
    {
        super.init(config);
        System.out.println(" Servlet SecurityCheckAdmin : Initialized");
    }

    protected void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        request.setCharacterEncoding("UTF-8");
        //request.setCharacterEncoding("ISO-8859-1");
        HttpSession session = request.getSession(true);
       
       
        if(request.getParameter("delete").equals("yes")){ //get new orders
           
            File file = new File("orders.csv");
            file.delete();
            RequestDispatcher rd = getServletContext().getRequestDispatcher("/admin.jsp");
            rd.forward(request, response);
        }
       
            if(request.getParameter("next").equals("new")){
           
            Connection conn = getConnection();
           
                try{

                Statement statement = conn.createStatement();
                ResultSet res = statement.executeQuery("select Count(new) as count from visitkort where new = '1'");//
             
                res.next();
                if(res.getInt(1) == 0){
                    RequestDispatcher rd = getServletContext().getRequestDispatcher("/noOrders.jsp");
                    rd.forward(request, response);
                }
         
                }catch(SQLException e){
                    e.printStackTrace();
                    System.out.println(e.toString());
                     
                }
           
           
            File file = retrieveCSVOrders(conn, "SELECT * FROM visitkort WHERE new LIKE '1'");
            updateOrders(conn,"UPDATE visitkort SET new = '0' WHERE new = '1'");
 
        }
       
        if(request.getParameter("next").equals("all")) { // get all orders
            Connection conn = getConnection();
           
            File file = retrieveCSVOrders(conn, "SELECT * FROM visitkort");
            updateOrders(conn,"UPDATE visitkort SET new = '0' WHERE new = '1'");
                       
        }

       
      RequestDispatcher rd = getServletContext().getRequestDispatcher("/orders.jsp");
      rd.forward(request, response);

       
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        doPost(request, response);
    }

    public void destroy()
    {
        try
        {
            statement.close();
            connection.close();
            ctx.close();
            System.out.println(" Servlet afmeld : Destroyed");
        }
        catch (SQLException e){System.out.println("Error destroying servlet : "+e);}
        catch(NamingException ne){System.out.println("Error destroying servlet : "+ne);}
       
 
    }
   
    private Connection getConnection(){
       
       
     
        try
        {
            ctx = new InitialContext();
            Object o = ctx.lookup("defaultPool");
            DataSource ds = (DataSource)PortableRemoteObject.narrow(o, DataSource.class);
            connection = ds.getConnection();
           
           
        }
        catch (SQLException e){System.out.println(e);}
        catch(NamingException ne){System.out.println(ne);}
        catch(Exception ee){ee.printStackTrace();}
   
        return connection;
    }

    protected File retrieveCSVOrders(Connection conn, String SQL)
    {
       
        String orders ="Navn;Titel;Arbejdsplads;Adresse;Postnummer;By;Telefon;Email;Tid1;Tid2;Tid3;Bestillingsdato"+"\n";
        String tid1;
        String tid2;
        String tid3;
        File file = null;
       
        try{
            file = new File("orders.csv");
            FileWriter outFile = new FileWriter(file);
            BufferedWriter out = new BufferedWriter(outFile);
            StringBuffer buffer = new StringBuffer();
           
           
     
            // lav CSV liner her
           
            Statement statement = conn.createStatement();
            ResultSet res = statement.executeQuery(SQL);//
           
            if(res==null){
            }
            // sorts the resultset and places the various recepients into their respective groups and assigns the to an arraylist
            while(res.next()){
           
            if (res.getString(9) == null){
                tid1 = "";
            } else {
                tid1=res.getString(9);
            }
           
            if (res.getString(10) == null){
                tid2 = "";
            } else {
                tid2=res.getString(10);
            }
           
            if (res.getString(11) == null){
                tid3 = "";
            } else {
                tid3=res.getString(11);
            }
           
           
           
            orders += ""+res.getString(2)+";\"rådgiver\";\""+res.getString(3)+"\";\""+res.getString(4)+"\";\""+res.getInt(5)+"\";\""+res.getString(6)+"\";\""+res.getString(7)+"\";\""+res.getString(8)+"\";\""+tid1+"\";\""+tid2+"\";\""+tid3+"\";\""+res.getString(12)+"\""+"\n";
                   
            }
     
           
            buffer.append(orders);
            buffer.append(System.getProperty("line.separator"));
           
               
            out.write(buffer.toString());
            buffer = null;
            out.flush();           
            out.close();
            outFile.close();
           
     
     
     
        }catch(SQLException e){
            e.printStackTrace();
            System.out.println(e.toString());
             
        }catch(IOException ee){
        System.out.println(ee.toString());
        ee.printStackTrace();
           
        }
       
        return file;
    }
   
    private void updateOrders(Connection conn, String SQL){
       
        try{
          Statement statement = conn.createStatement();
          statement.executeUpdate(SQL);
          conn.close();
     
     
     
        }catch(SQLException e){
            e.printStackTrace();
            System.out.println(e.toString());
             
        }
       
    }
   

       
}
Avatar billede arne_v Ekspert
11. marts 2008 - 15:15 #1
Jeg tror at connection leak er fordi at:
- RetrieveOrders service aabner en connection for hver request
- connection lukkes kun en enkelt gang i RetrieveOrders destroy naar serveren lukker ned
Avatar billede arne_v Ekspert
11. marts 2008 - 15:15 #2
Og saa er koden bad bad bad.
Avatar billede arne_v Ekspert
11. marts 2008 - 15:18 #3
Du behover ikke narrow'e en DataSource da den altid er local og ikke remote.

System.out.println er absolut no no i J2EE/JavaEE.

Der testes ikke paa retur varedi af res.next() kaldet.

De der instans variable i RetrieveOrders er en katastrofe med hensyn til thread safeness.
Avatar billede arne_v Ekspert
11. marts 2008 - 15:29 #4
Mix af dansk og engelsk er forvirrende.

Der er en del steder baade i koden og i databasen hvor der bruges strenge men hvor jeg tror
at en anden data type ville vaere bedre.
Avatar billede zamiel Nybegynder
11. marts 2008 - 16:59 #5
Lidt ulogisk så ser det ud til at forbindelsen lukkes i metoden updateOrder i RetrieveOrders.. men ja det er ikke så pæn kode. :)
Avatar billede arne_v Ekspert
11. marts 2008 - 17:25 #6
Det har du ret i.

Men det sker ikke i tilfaelde af en fejl.
Avatar billede zamiel Nybegynder
11. marts 2008 - 18:02 #7
ok, så du siger at den løber tør for connections fordi den ikke releaser dem når der kommer exceptions? fair nok..

men så tænker jeg, fejlene kommer jo fordi der ingen connections er.. De her fejl kan jo ikke opstå hvis der stadig er connections i connection poolen.
Avatar billede arne_v Ekspert
11. marts 2008 - 18:09 #8
Nu er jeg forvirret.

app serveren laver N connections ved opstartog smider dem i pool
DataSource getConnection henter en connection fra pool og markerer den som i brug
close markerer connection i pool som ledig

Hvis man ikke faar kaldt close i nogle tilfaelde, saa ender alle connections i pool
med at vaere markeret i brug.

Og saa faar man en exception.
Avatar billede zamiel Nybegynder
11. marts 2008 - 18:44 #9
der kan jeg godt følge dig, men i hvilke tilfælde får den ikke lukket? Du skrev at connection ikke bliver lukket ved en fejl. Men hvis der ikke opstår en fejl burde den så ikke altid få lukket de respektive connections?

Jeg har kigget logfilerne igennem for den sidste uge og alle fejl der kommer er fordi at connectionpool er tom til at starte med..
Avatar billede zamiel Nybegynder
11. marts 2008 - 18:45 #10
dvs at der ikke har været nogle fejl som kunne have startet denne reaktion som skulle ende med at connectionpoolen er tom..
Avatar billede zamiel Nybegynder
11. marts 2008 - 18:46 #11
hmm jeg ser lige at statement og initialcontext objectet ikke bliver lukket.. kan det være en del af årsagen?
Avatar billede arne_v Ekspert
12. marts 2008 - 00:16 #12
Umiddelbart vil jeg mene nej.

Er det kun de 2 servlets som kan være skyld i leaken ?
Avatar billede zamiel Nybegynder
12. marts 2008 - 13:00 #13
Nej, der er en til men der bliver connection også lukket i finally



public class bestilVisitKort extends HttpServlet
{
        Connection connection = null;
        Statement statement = null;
        Context ctx = null;
        ResultSet res = null;
        String sql="";
        boolean zero = true;
        String next="/confirmation.jsp";
   
    public void init(ServletConfig config) throws ServletException
    {
        super.init(config);
    }
   
    public void service(HttpServletRequest request, HttpServletResponse response)   
    throws ServletException, IOException
    {
       
   
        request.setCharacterEncoding("ISO-8859-1");
       
     
       
        try
        {
            ctx = new InitialContext();
            Object o = ctx.lookup("defaultPool");
            DataSource ds = (DataSource)PortableRemoteObject.narrow(o, DataSource.class);
            connection = ds.getConnection();
            statement = connection.createStatement();
        }
        catch (SQLException e){System.out.println(e);}
        catch(NamingException ne){System.out.println(ne);}
       
        HttpSession session = request.getSession();
        if(session.getAttribute("user") == null) next = "/index.jsp";
        else
        {
            Calendar cal = Calendar.getInstance();
            SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yy hh:mm:ss");
            String date = formatter.format(cal.getTime()).toString();
           
           
            try
            {
                sql = "INSERT INTO visitkort VALUES (visitkort_sequence.nextval,'"+request.getParameter("navn")+"','"+request.getParameter("arbejde")+"','"+request.getParameter("adresse")+"','"+request.getParameter("postnummer")+"','"+request.getParameter("bynavn")+"','"+request.getParameter("telefon")+"','"+request.getParameter("email")+"','"+request.getParameter("tid1")+"','"+request.getParameter("tid2")+"','"+request.getParameter("tid3")+"','"+date+"','1')";
                statement.executeUpdate(sql);
               
            }
            catch(SQLException se)
            {
                System.out.println(se);
                request.setAttribute("Error",se);
                next = "/error.jsp";
            }
            finally
            {
                try
                {
                    statement.close();
                    connection.close();
                    ctx.close();
                }
                catch (SQLException e){System.out.println(e);}
                catch(NamingException ne){System.out.println(ne);}
            }
        }
       
        RequestDispatcher rd;
        rd = getServletContext().getRequestDispatcher(next);
        rd.forward(request,response);
    }
   
    public void sendMail (String to, String from, String subject, String content)
    {
        try
        {
            Authenticator  authenticator = null;
            Properties  properties = new Properties ();
            properties.put ("mail.smtp.host","localhost");
            javax.mail.internet.MimeMessage  msg = new javax.mail.internet.MimeMessage (
            javax.mail.Session.getInstance ( properties, authenticator) );
           
            msg.setRecipients (Message.RecipientType.TO,to);
            msg.setFrom (new InternetAddress(from));
            msg.setSubject(subject,"ISO-8859-1");
            msg.setHeader("Content-Language","dk");
            msg.setContent(content,"text/html; charset=ISO-8859-1");
            Transport.send ( msg );
        }
        catch (Exception ex){System.out.println(ex);}
    }
   
    public void doGet(HttpServletRequest request, HttpServletResponse response)   
    throws ServletException, IOException
    {
        doPost(request,response);
    }
   
    public void destroy(){
        try
        {
            statement.close();
            connection.close();
            ctx.close();
            System.out.println(" Servlet afmeld : Destroyed");
        }
        catch (SQLException e){System.out.println("Error destroying servlet : "+e);}
        catch(NamingException ne){System.out.println("Error destroying servlet : "+ne);}
       
    }
}
Avatar billede arne_v Ekspert
16. marts 2008 - 01:20 #14
Den tror jeg kan lække connections.

connection er en instans variabel og den er ikke thread safe.

Det virker med:

thread 1

connection = ds.getConnection();
statement = connection.createStatement();
statement.executeUpdate(sql);
statement.close();
connection.close();

men:

thread 1                                              thread 2
connection = ds.getConnection();
statement = connection.createStatement();
statement.executeUpdate(sql);
                                                      connection = ds.getConnection();
                                                      statement = connection.createStatement();
                                                      statement.executeUpdate(sql);

statement.close();
connection.close();
                                                      statement.close();
                                                      connection.close();

leaker en connection.

Den først hentede connection bliver ikke closed.

At den sidst hentede connection bliver closed 2 gange kompenserer ikke for det.
Avatar billede zamiel Nybegynder
17. marts 2008 - 22:41 #15
i så tilfælde vil jeg kun se en fejl hvis flere brugere åbner en connection samtidigt,hvilket er hvorfor jeg ikke selv har kunnet genskabe fejlen.

Hvad er så best practice for at undgå connection leak?
Avatar billede zamiel Nybegynder
17. marts 2008 - 22:59 #16
btw hvis du lægger et svar så får du nogle point
Avatar billede arne_v Ekspert
18. marts 2008 - 00:19 #17
Connection som lokal variabel
close i finally
Avatar billede arne_v Ekspert
18. marts 2008 - 00:19 #18
og et svar
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester