Monday, September 1, 2014

Developing a simple rss feed aggregator web application

Developing a simple RSS Feed Aggregator web application:

Let us see how we can develop a very simple RSS Feed aggregator application in easy steps>>

Prerequisites: 

Following jar files are required in order to use feed api:

  1. jdom-1.1.1.jar
  2. purl-org-content-0.3.jar
  3. rome-1.0.0.jar
Following jquery library is required:
  • jquery-1.9.1.js [Note: you can use any version of jquery library but should be 1.9+]
Steps required:
  • Create a dynamic web project, for example: "RSSFeedAggregator"
  • Create a jsp file for example: 'index.jsp'
  • Add the entry of this jsp file to web.xml as given below:
<web-app>
  <display-name>RSSFeedAggregator</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

  • Add the following code to "index.jsp" page:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>RSSFeedAggregator</title>
<script src="js/jquery-1.9.1.js" type="text/javascript"></script>
<script src="js/common.js" type="text/javascript"></script>

<script type="text/javascript">
$(document).ready(function() {
setInterval(synchRSSFeeds, 15000); //Refresh the page after 15000  
});
/*
 * Function is used to call ajaxRequest 
 */
function ajaxRequest(actionName, dataUrl, callBack,options) {
var optionsLocal = {
"showSpinner" : true
};
optionsLocal = $.extend(optionsLocal, options);
$.ajax({
url : actionName,
type : "POST",
data : dataUrl,
dataType : "html",
cache : false,
context : document.body,
success : function(data) {
if (callBack) {
callBack(data, options);
}
},
error : function(xhr, ajaxOptions, thrownError) {
alert(xhr.status);
    alert(thrownError);
}
});
}

function synchRSSFeeds(){
ajaxRequest("RSSReaderServlet", "", syncRSSFeedsManp, {"showSpinner":false});
}

function syncRSSFeedsManp(data){
$("#feeds").html(data);
}

</script>
<style type="text/css">
.footernew {
background-repeat: no-repeat;
height: 65px;
font-size: 12px !important;
color: black !important;
width: 927px;
}

#pagewrap {
min-width: 80%;
margin-left: auto;
margin-right: auto;
width: 980px;
margin: 20px auto 0px;
}

.gradientBoxesWithOuterShadows {
height: 420px;
width: auto;
padding: 5px;
background-color: lightgray;
/* outer shadows  (note the rgba is red, green, blue, alpha) */
-webkit-box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 0px 1px 6px rgba(23, 69, 88, .5);
/* rounded corners */
-webkit-border-radius: 12px;
-moz-border-radius: 7px;
border-radius: 7px;
/* gradients */
/*old color code- #D7E9F5*/
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, white),
color-stop(15%, white), color-stop(100%, #BDBDC9));
background: -moz-linear-gradient(top, white 0%, white 55%, #BDBDC9 130%);
width: auto;
padding: 5px;
}
.imglogo {
height: 30px;
width: 170px;
float: left;
border: 0;
}

</style>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<link rel="icon" href="favicon.ico" type="image/x-icon">
</head>
<body bgcolor="#EDEDED">
<DIV id="pagewrap">
   <img id="logo" src="your-comp-logo.jpg" class="imglogo" />
<%-- DISPLAY THE FEEDS HERE --%>
<MARQUEE behavior="scroll" direction="left">
<h2>RSS Feeds ...</h2>
</MARQUEE>
<hr/>
<div class="gradientBoxesWithOuterShadows">
<div id="feedsdiv">
<marquee behavior="scroll" direction="down" scrollamount="2" height="400px"
 onmouseover="this.stop();" onmouseout="this.start();">
<div id="feeds"></div>
</marquee>
</div>
</div>
<table cellpadding="0" cellspacing="0" align="center"
style="padding-top: 5px">
<tr>
<td class="footernew">
<table cellpadding="0" cellspacing="0" width="100%" class="normal">
<tr>
<td align="center">Copyright Info</td>
</tr>
</table>
</td>
</tr>
</table>
</DIV>
</body>
</html>

  • Create a FeedAggregator servlet for e.g. 'RSSReaderServlet'
package com.rss.feedservices;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.rss.model.FEEDMessage;
import com.rss.utils.PubDateComparator;

@WebServlet("/RSSReaderServlet")
public class RSSReaderServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
StringBuffer feedsContent = new StringBuffer("<table border='1'>");
feedsContent
.append("<tr><th>Feeds</th><th>Published Date</th><th>Author</th></tr>");
RSSReaderHelper feedHlpr = new RSSReaderHelper();
List<FEEDMessage> feeds_gen = feedHlpr.getRSSFeeds("http://example.com/rss/feeds?id=544554544");
        List<FEEDMessage> feeds=new ArrayList<FEEDMessage>();
        feeds.addAll(feeds_gen);
        Collections.sort(feeds,new PubDateComparator());
for (Iterator<FEEDMessage> iterator = feeds.iterator(); iterator
.hasNext();) {
FEEDMessage feedMessage = iterator.next();
feedsContent
.append("<tr><td width='70%'><a target='_blank' href='")
.append(feedMessage.getFeedLink())
.append("'>")
.append(feedMessage.getFeedTitle())
.append("</a></td><td width='20%' align='center'>")
.append(feedMessage.getFeedPubDate())
.append("</td><td width='10%' align='center'>")
.append(feedMessage.getFeedAuthor() == "" ? "Abhinav"
: feedMessage.getFeedAuthor()).append("</td></tr>");
}
feedsContent.append("</table>");
response.getWriter().write(feedsContent.toString());
}
}

  • Create a helper class which we gonna use in the servlet to fetch the feeds e.g. 'RSSReaderHelper'.
package com.rss.utils;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.rss.model.FEEDMessage;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.io.SyndFeedInput;
import com.sun.syndication.io.XmlReader;

public class RSSReaderHelper {
public List<FEEDMessage> getRSSFeeds(String feedUrl)
throws MalformedURLException {
URL url = new URL(feedUrl);
FEEDMessage feedMsg = null;
List<FEEDMessage> feedList = new ArrayList<FEEDMessage>();
try (XmlReader reader = new XmlReader(url);) {
SyndFeed syndFeed = new SyndFeedInput().build(reader);
for (Iterator<SyndEntry> itr = syndFeed.getEntries().iterator(); itr.hasNext();) {
SyndEntry entry = itr.next();
feedMsg = new FEEDMessage();
feedMsg.setFeedAuthor(entry.getAuthor());
feedMsg.setFeedLink(entry.getLink());
feedMsg.setFeedTitle(entry.getTitle());
feedMsg.setFeedContents(entry.getContents());
feedMsg.setFeedUri(entry.getUri());
feedMsg.setFeedUpdateDate(entry.getUpdatedDate());
feedMsg.setFeedPubDate(entry.getPublishedDate());
feedMsg.setFeedDESC(entry.getDescription());
feedList.add(feedMsg);
}
} catch (MalformedURLException mfe) {
mfe.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return feedList;
}
}

  • Create a model class that will hold feeds e.g. "FEEDMessage"
package com.rss.model;
import java.util.Date;
import java.util.List;
import com.sun.syndication.feed.synd.SyndContent;
import com.sun.syndication.feed.synd.SyndEntry;
public class FEEDMessage {
private List<SyndEntry> feedContents;
private SyndContent feedDESC;
private String feedAuthor;
private String feedTitle;
private String feedLink;
private String feedUri;
private Date feedUpdateDate;
private Date feedPubDate;

................
......... //setter & getter 

}

  • Create a date comparator class to sort the feeds on latest date e.g PubDateComparator.
package com.rss.utils;
import java.util.Comparator;
import com.rss.model.FEEDMessage;
public class PubDateComparator implements Comparator<FEEDMessage> {
@Override
public int compare(FEEDMessage o1, FEEDMessage o2) {
return o2.getFeedPubDate().compareTo(o1.getFeedPubDate());
}
}






How database drivers loaded to system ?

So, we all know how to create a JDBC connection, but what happens when you do
Class.forName (xxxx.xxxx.XXXDriver).

Let's have a look...


The DriverManager class maintains a list of Driver classes that have registered themselves by calling the method DriverManager.registerDriver. All Driver classes should be written with a static section (a static initializer) that creates an instance of the class and then registers it with the DriverManager class when it is loaded. Thus, a user would not normally call DriverManager.registerDriver directly; it should be called automatically by a Driver class when it is loaded. A Driver class is loaded, and therefore automatically registered with the DriverManager.

Most JDBC Driver classes register themselves in their static initializers by calling registerDriver().
registerDriver() is the real call that you hardly ever need to call yourself (unless you write your own JDBC driver).

It can be done using:
Class.forName (com.microsoft.jdbc.sqlserver.SQLServerDriver);
 
Calling the Class.forName automatically creates an instance of a driver and registers it with 
the DriverManager,  so you don't need to create an instance of the class,
If you were to create your own instance, you would be creating an unnecessary duplicate, 
but it would do no harm.

After this it is in the DriverManager's list of drivers and available for creating a connection.
 
So SQLServerDriver written with a static section (a static initializer) that
creates an instance of the class and then registers it with the DriverManager class when it is loaded.


See the given snippet
 
public final class SQLServerDriver implements Driver
{

    static final String driverProperties[] = {
        "databaseName", "user", "password", "serverName", "portNumber", "disableStatementPooling", "integratedSecurity", "lockTimeout", "sendStringParametersAsUnicode", "lastUpdateCount",
        "applicationName", "selectMethod", "loginTimeout", "instanceName", "workstationID", "xopenStates"
    };
    static final String driverPropertiesSynonyms[][] = {
        {
            "database", "databaseName"
        }, {
            "userName", "user"
        }, {
            "server", "serverName"
        }, {
            "port", "portNumber"
        }
    };

  static Properties connProperties;

  static
  {
        try
        {
            DriverManager.registerDriver(new SQLServerDriver());
        }
        catch(SQLException sqlexception)
        {
            sqlexception.printStackTrace();
        }
    }

    public SQLServerDriver()
    {
    }

…….all other methods..

}//end of SQLServerDriver   
 
 
Does loading of two driver classes at same time effect the connection and how to load a driver using static loading:
 
NO, It does not affect database connection it will just register them self with driver manager class by calling
DriverManager.registerDriver() method.
 
Because Vendor specific drivers are implemented in such a way that after loading them using
Class.forName(“”) will automatically register that driver to DriverManager which maintains the 
list of drivers available.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import com.mysql.jdbc.Driver;

public class DBConnection
{
  public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
        Class.forName("com.mysql.jdbc.Driver");
        //com.mysql.jdbc.Driver d= new Driver(); Drivers can be loaded in this way also.
        Connection con= DriverManager.getConnection("jdbc:mysql://localhost:3306/usermgmtdb", "root", "root");
        System.out.println("MySQLConnection object: "+con);
        Enumeration e = DriverManager.getDrivers();
        while (e.hasMoreElements())
        {
              System.out.println("Registered jdbc drivers to driver manager: "+e.nextElement());
        }
  }
}
 
OUTPUT>>
 
MySQLConnection object: com.mysql.jdbc.Connection@18558d2
Registered jdbc drivers to driver manager: sun.jdbc.odbc.JdbcOdbcDriver@14318bb
Registered jdbc drivers to driver manager: com.mysql.jdbc.Driver@530daa


JDBC Drivers can also be loaded in this way also:
 
com.mysql.jdbc.Driver d= new Driver();
This will also load the mysql driver to DriverManager.
 
But Class.forName(“”) is recommended to load the driver.
 
Reason behind it is >>
 
A call to Class.forName("X") causes the class named X to be dynamically loaded (at runtime). 
A call to forName("X") causes the class named X to be initialized (i.e., JVM executes all
its static block after class loading). Class.forName("X") returns the Class object associated with the "X" class.
Class.forName("X") loads the class if it not already loaded. 
The JVM keeps track of all the classes that have been previously loaded.
This method uses the classloader of the class that invokes it. 
The "X" is the fully qualified name of the desired class.
For example,

class AClass { 
    static { 
        System.out.println("static block in AClass");
    }
}

public class Program { 

    public static void main(String[] args) {

        try { 

            Class c   = Class.forName("AClass"); 

        } catch (ClassNotFoundException e) {

        }
    }
}
The output is
static block in AClass
Here is one example that uses returned Class to create an instance of AClass:


class AClass {

  public AClass() {

    System.out.println("AClass's Constructor");
  }

  static { 
    System.out.println("static block in AClass");
  }

}


public class Program { 

  public static void main(String[] args) {

    try { 

      System.out.println("The first time calls forName:");
      Class c   = Class.forName("AClass"); 
      AClass a = (AClass)c.newInstance();

      System.out.println("The second time calls forName:");

      Class c1 = Class.forName("com.xyzws.AClass");

    } catch (ClassNotFoundException e) {
           e.printStacktrace();

    } catch (InstantiationException e) {
         e.printStacktrace();
    } catch (IllegalAccessException e) {            e.printStacktrace();     }   } }
The output is
The first time calls forName:

static block in AClass

AClass's Constructor

The second time calls forName:  

//Calss has been loaded so there is not "static block in AClass" printing out
 
 

 

Friday, August 29, 2014

Type conversion of integers in Java


Let's see how integer type are converted in java internally.


Type conversion of integers in Java:

Converting from int to byte and short >>
Example>
int num=132;
byte b= (byte)num;
short s= (short) num;
------------------------------------------------------------------------------------------------------------

Explanation---
Note :>> in java byte (8-bit), short (16-bit), int (32-bit), long (64-bit) are signed 2's complement Integers,
I.e. these data types stores data in form of 2's complement, because java supports signed    integers and to store the negative values it uses the 2’s complement of their binary representation.

byte b= (byte)num;

0000 0000 0000 0000 0000 0000 1000 0100 => 32 bit int

1000 0100 => 8 bit byte [Rest of 24 bits will be chopped off to make it 8 bit from 32 bit, because 132 will not fit within the range of byte which is -128 to 127]
Here 2's complement will be taken---

0111 1011  (1s compliment)
              +1   (2s complement)
0111 1100
----------------
- 124 (byte)
----------------
*************************************************************************************
short s= (short) num;

0000 0000 1000 0100 => 16 bit short [No loss of bit so result will be same, i.e. 132, because 132 is perfectly fits within the range of short which is -32768 to 32767]
Now  Let, short s1= (short) 32769;
So,
0000 0000 0000 0000 1000 0000 0000 0001  => 32769 (an int value)

1000 0000 0000 0001  => (Rest of the bits which are out of range are chopped off here)
0111 1111 1111 1110 (1's complement)
                                  +1 (2's complement)
----------------------------
0111 1111 1111 1111 => - 32767 (short)
----------------------------


Converting from int (hex) to byte and short >>
Example>
int a=0xf1;
byte b= (byte)a;
------------------------------------------------------------------------------------------------------------

Explanation---
Note :>> in java byte (8-bit), short (16-bit), int (32-bit), long (64-bit) are signed 2's complement Integers,
I.e. these data types stores data in form of 2's complement, because java supports signed    integers and to store the negative values it uses the 2’s complement of their binary representation.

Here, int a=0xf1 , represents hexadecimal representation of int value, which is equal to 241.

So,
0000 0000 0000 0000 0000 0000 1111 0001 => 241 (an int value 32 bit)

1111 0001 => bit byte [Rest of 24 bits will be chopped off to make it 8 bit from 32 bit, because 241 will not fit within the range of byte which is -128 to 127]

0000 1110  (1’s compliment)
              +1   (2’s complement)
0000 1111
----------------
- 15 (byte)
----------------
Remember: 2’s complement will be taken when you convert the int to byte and short, or long to int.

Converting from long (hex) to int >>
Example:
long a= 0xffffffff;
int b= (int)a;
-----------------------------------------------------------------------------------------------------------

Explanation---
Note :>> in java byte (8-bit), short (16-bit), int (32-bit), long (64-bit) are signed 2's complement Integers,
I.e. these data types stores data in form of 2's complement, because java supports signed    integers and to store the negative values it uses the 2’s complement of their binary representation.
Here, long a=0xffffffff, represents hexadecimal representation of long value,which is equal to 4294967281.
So,
0000 0000 0000 0000 0000 0000 0000 0000 1111 1111 1111 1111 1111 1111 1111 1111 >>>> 4294967281 [64 bit long]

In int>> 1111 1111 1111 1111 1111 1111 1111 1111 [Rest of the bit are chopped off to fit the digits in the range of int,which is 2-32 TO 2 31]

0000 0000 0000 0000 0000 0000 0000 0000 -- 1's complement
                                                                                +1  -- 2's complement
0000 0000 0000 0000 0000 0000 0000 0001
---------------------------------------------------------
  -1 (int)
----------------------------------------------------------

Monday, August 25, 2014

Traverse directories recursively and get the sorted URIs

Here is the utility class which can be used to traverse directories recursively and get the sorted URIs::

import java.io.File;

import java.io.FileNotFoundException;

import java.util.Arrays;

import java.util.Iterator;

import java.util.List;

import java.util.Set;

import java.util.TreeSet;

 

/**

* This class DirectoryTraverser.<br/>

* It returns set of files by traversing directories recursively.

*

* @since 2014

* @author Abhinav Kumar Mishra

*/

public final class DirectoryTraverser {

 

 /**

 * Gets the file URIs. Recursively traverse each directory and uris of files.

 *

 * @param aStartingDir the a starting dir

 * @return the file listing

 * @throws FileNotFoundException the file not found exception

 */

 public static Set<File> getFileUris(final File aStartingDir)

  throws FileNotFoundException {

  checkDirectories(aStartingDir); // throw exception if not valid.

  return getUrisRecursive(aStartingDir);

 }

 

 /**

 * Gets the file uris recursively.

 *

 * @param aStartingDir the a starting dir

 * @return the file listing no sort

 * @throws FileNotFoundException the file not found exception

 */

 private static Set<File> getUrisRecursive(final File aStartingDir)

  throws FileNotFoundException {

  final Set<File> sortedSetOfFiles = new TreeSet<File>();

  final File[] filesAndDirs = aStartingDir.listFiles();

  final List<File> filesDirs = Arrays.asList(filesAndDirs);

  final Iterator<File> filesDirsItr = filesDirs.iterator();

  while (filesDirsItr.hasNext()) {

   final File file = filesDirsItr.next();

   sortedSetOfFiles.add(file); // Add files and directory URIs both

   // If uri is a directory the revisit it recursively.

   if (!file.isFile()) {

    // Call 'getUrisRecursive' to extract uris from directory

    final Set<File> innerSet = getUrisRecursive(file);

    sortedSetOfFiles.addAll(innerSet);

   }

  }

  return sortedSetOfFiles;

 }

 

 /**

 * Checks if is valid directory.<br/>

 * If directory exists then it is valid. If directory is valid then it can

 * be read.

 *

 * @param directoryUri the a directory

 * @throws FileNotFoundException

  */

 private static void checkDirectories(final File directoryUri)

                            throws FileNotFoundException {

  if (directoryUri == null) {

   throw new IllegalArgumentException("Directory should not be null.");

  }if (!directoryUri.exists()) {

    throw new FileNotFoundException("Directory does not exist: "+ directoryUri);

  }if (!directoryUri.isDirectory()) {

   throw new IllegalArgumentException("Is not a directory: "+ directoryUri);

  }if (!directoryUri.canRead()) {

   throw new IllegalArgumentException("Directory cannot be read: "

                       + directoryUri);

  }

 }

 

        public static void main(String[] args) {

  try {

   System.out.println(getFileUris(new File("c:\\intel")));

  } catch (FileNotFoundException e) {

   e.printStackTrace();

  }

 }

}



Output>>
Let directory uri is: C:\Intel

Result:

[c:\intel\ExtremeGraphics,
c:\intel\ExtremeGraphics\CUI,
c:\intel\ExtremeGraphics\CUI\Resource,
c:\intel\Logs,
c:\intel\Logs\IntelChipset.log,
c:\intel\Logs\IntelGFX.log,
c:\intel\Logs\IntelGFXCoin.log]


Sunday, July 27, 2014

Why closing external resources after using it, If GC can take care?


It is strongly recommend that you always close the connection when you are finished using it so that the connection will be returned to the pool. Connections are a limited and relatively expensive resource. Any new connection you establish that has exactly the same connection string, will be able to reuse the connection from the pool. If we don't close a DB connection, we get something called a "connection leak" once threshold is reached.


Now come to the GC, GC Cannot guarantee of its execution, neither you can force JVM to run GC on your method (in case you don't close connection.) because GC is a demon thread and hence it has lowest priority. it may execute if it gets the CPU at that time.

So not closing connections and leaving it for GC may result in many problems like web pages hanging, slow page loads, and more.


Also, you should be careful while closing the connections or stream etc. because even though you are closing connection , sometimes it leads to resource leakage if not handled properly. 

Lets take an example: 

public static Properties loadProperties(String fileName) 
throws IOException { 
FileInputStream fis = new FileInputStream(fileName); 
Properties props = new Properties(); 
props.load(fis); 
fis.close(); 
return props; 


In the above example, 'fis' is closed but it still prone to resource leakage. 

How? 
>>It because, if any exception occurred at the call of "load" method only.. method will throw exception but will not release the resource properly. 

Correct way:::: 

public static Properties loadProperties(String fileName) 
throws IOException { 
FileInputStream fis= new FileInputStream(fileName); 
try { 
Properties props = new Properties(); 
props.load(fis); 
return props; 

finally { 
fis.close(); 



And JDK 7 Onwards, you don’t have to be worried about closing resources. You can use TRY,CATCH with resources functionality. 

static String readFirstLineFromFile(String path) throws IOException { 
try (BufferedReader br = 
new BufferedReader(new FileReader(path))) { 
return br.readLine(); 
// Use br…
 }  catch(Exception excp){….}


And one last point... 

Suppose you are using JDBC connection,statement and resultset. Then you should close all 3 resources not only connection. 

And the best way to handle this is>> 

public void someOperation() throws SQLException { 
Statement stmt= null; 
ResultSet rs= null; 
Connection conn= getConnection(); 
try { 
stmt= conn.createStatement(); 
rs= statement.executeQuery("SELECT * FROM EMP"); 
// Use RS 

finally { 
try { 
if (rs!= null) 
rs.close(); 

finally { 
try { 
if (stmt != null) 
stmt.close(); 

finally { 
conn.close(); 



}


Now one more question,  Why to close ResultSet and Statement ??
You must have heard about bad programming practices. So, It is a best programming practice to close ResultSet, Statement along with Connection.

How>> ?

If you are using JDBC connection and not closing ResultSet object it will be automatically closed. So, closing it in finally block will releases this ResultSet object immediately instead of waiting for this to happen when it is automatically closed. Closing ResultSet explicitly enables garbage collector to recollect memory as early as possible because ResultSet object may take lot of memory depending on query.

As per java doc of ResultSet object::

"A ResultSet object is automatically closed when the Statement object that generated it is closed, re-executed, or used to retrieve the next result from a sequence of multiple results."

Also note that, you can get only one ResultSet object at a time from a Statement. So all execution methods in the Statement interface implicitly close a statment's current ResultSet object if an open one exists.

So, now Question is why statement should be closed?

As mentioned above the ResultSet will be closed automatically only if you close the Statement object. Not doing so will lead to resource leak.

This is the reason why JDBC API provided close() method for both ResultSet and Statement. These methods are not showpiece is'nt ?


Now, it’s totally depends on you whether you are writing a good quality code or not.