001package bradleyross.j2ee.servlets; 002import java.io.*; 003import java.sql.Types; 004import javax.servlet.*; 005import javax.servlet.http.*; 006/** 007* Serves as a template for writing servlets that 008* display the contains of a java.sql.ResultSet object 009* as a CSV (comma separated values) page. 010* <p>The methods getResultSet and makeConnection are 011* abstract and will need to be overridden before 012* this class can be used. The user may also wish to 013* override several of the methods for formatting 014* information.</p> 015* <p>This is the class that is currently used by 016* com.amtrak.eng.servlets.downloadDataServlet and 017* com.amtrak.eng.tests.testServlet. After 018* bradleyross.library.servlets.csvWriter is modified 019* to make the variables local to the service method, 020* the classes in com.amtrak.eng should be modified to 021* make use of that version.</p> 022* @author Bradley Ross 023* @deprecated 024*/ 025public abstract class csvWriter2 extends HttpServlet 026 { 027 /** 028 * 029 */ 030 private static final long serialVersionUID = 1L; 031/** 032 * File name to be used when creating file. File will be 033 * suffixed with .csv. 034 * <p>Changes to this field can be placed in the method 035 * getResultSet.</p> 036 * @see #getResultSet(javax.servlet.http.HttpServletRequest) 037 */ 038 protected String outputFilename = "default"; 039 /** 040 * String to be placed in CSV file as the first line in the file. 041 * <p>Changes to this field can be placed in the method 042 * getResultSet.</p> 043 * @see #getResultSet(javax.servlet.http.HttpServletRequest) 044 */ 045 protected String message = null; 046 /** 047 * Object containing database connection information. 048 * <p>This field is set by the makeConnection method.</p> 049 * @see #makeConnection() 050 */ 051 protected bradleyross.library.database.DatabaseProperties data = null; 052 /** 053 * Object containing the result set. 054 * <p>This object is created by the getResultMethod method which 055 * is called by the service method. 056 */ 057 protected java.sql.ResultSet rs = null; 058 /** 059 * Object containing meta data on result set. 060 * <p>This object is created by the service method after the 061 * execution of the getResultSet method and before the 062 * execution of the printHeader method.</p> 063 */ 064 protected java.sql.ResultSetMetaData rsmd = null; 065 /** 066 * Object representing the output to the HTTP response. 067 */ 068 protected java.io.PrintWriter output = null; 069 /** 070 * Number of columns in result set 071 */ 072 protected int columnCount; 073 /** 074 * ServletConfig object as passed to the init method. 075 */ 076 protected ServletConfig config = null; 077 public void init(ServletConfig configIn) throws ServletException 078 { 079 super.init(configIn); 080 config = configIn; 081 } 082 public void destroy() 083 { super.destroy(); } 084 /** 085 * @param req Object containing request information 086 * @param res Object containing response information 087 */ 088 public void service (HttpServletRequest req, 089 HttpServletResponse res) throws IOException 090 { 091 try 092 { 093 data = makeConnection(); 094 data.connect(); 095 output = res.getWriter(); 096 rs = getResultSet(req); 097 res.setContentType("text/csv"); 098 res.setHeader("Content-Disposition", "attachment;filename=" + 099 outputFilename + ".csv"); 100 rsmd = rs.getMetaData(); 101 if (message != null) 102 { output.print(message + "\r\n"); } 103 columnCount = rsmd.getColumnCount(); 104 if (rs == null) 105 { 106 output.print("No result set provided\r\n"); 107 return; 108 } 109 boolean moreRecords = rs.next(); 110 if (!moreRecords) 111 { 112 output.print("Result set is empty\r\n"); 113 return; 114 } 115 else 116 { printHeader(); } 117 while (true) 118 { 119 printLine(); 120 moreRecords = rs.next(); 121 if (!moreRecords) { break; } 122 } 123 rs.close(); 124 data.close(); 125 } 126 catch (java.io.IOException e) 127 { throw new java.io.IOException("Error in processing file: " + 128 e.getClass().getName() + " : " + e.getMessage()); } 129 catch (java.sql.SQLException e) 130 { 131 System.out.println("SQL error in processing CSV file"); 132 throw new java.io.IOException("SQL error: " + e.getClass().getName() + 133 " : " + e.getMessage()); 134 } 135 } 136 /** 137 * Obtain the database connection information. 138 * @return database object 139 * @throws IOException if io error 140 */ 141 protected abstract bradleyross.library.database.DatabaseProperties 142 makeConnection() throws IOException; 143 /** 144 * Will be overridden with the method for obtaining the SQL 145 * result set. 146 * <p>Changes to fields such as message and outputFilename can go in this 147 * method.</p> 148 * @param req Request information 149 * @return Result set 150 * @throws IOException if io errors 151 * @see #message 152 * @see #outputFilename 153 */ 154 protected abstract java.sql.ResultSet getResultSet(HttpServletRequest req) 155 throws java.io.IOException; 156 /** 157 * Will print header line for CSV file. 158 * @throws IOException if io errors 159 */ 160 protected void printHeader () 161 throws java.io.IOException 162 { 163 try 164 { 165 for (int i = 1; i <= columnCount; i++) 166 { 167 if (i != 1) { output.print(","); } 168 output.print(rsmd.getColumnName(i)); 169 } 170 output.print("\r\n"); 171 } 172 catch (Exception e) 173 { throw new java.io.IOException("Error detected: " + e.getClass().getName() + 174 " : " + e.getMessage()); } 175 } 176 /** 177 * Will print one line of CSV file. 178 * <p>This method will be changed at a later time to allow the 179 * specification of overriding methods for formatting timestamps, 180 * integers, floating point numbers, character strings, etc.</p> 181 * @throws IOException if io errors 182 */ 183 protected void printLine () 184 throws java.io.IOException 185 { 186 for (int i = 1; i <= columnCount; i++) 187 { 188 try 189 { 190 if (i != 1) { output.print(","); } 191 int columnType = rsmd.getColumnType(i); 192 if (columnType == Types.VARCHAR || columnType == Types.CHAR || 193 columnType == Types.LONGVARCHAR) 194 { 195 String working = rs.getString(i); 196 if (rs.wasNull()) { working = new String(); } 197 output.print(formatString(working, i)); 198 } 199 else if (columnType == Types.FLOAT || columnType == Types.REAL || 200 columnType == Types.DOUBLE) 201 { 202 double working = rs.getDouble(i); 203 if (rs.wasNull()) 204 { output.print(new String()); } 205 else 206 { output.print(formatDouble(working)); } 207 } 208 else if (columnType == Types.TINYINT || columnType == Types.SMALLINT || 209 columnType == Types.INTEGER || columnType == Types.BIGINT) 210 { 211 int working = rs.getInt(i); 212 if (rs.wasNull()) 213 { output.print(new String()); } 214 else 215 { output.print(formatInteger(working)); } 216 } 217 else if (columnType == Types.TIMESTAMP) 218 { 219 java.sql.Timestamp working = rs.getTimestamp(i); 220 if (rs.wasNull()) 221 { output.print(new String()); } 222 else 223 { 224 java.util.Date date = new java.util.Date(working.getTime()); 225 output.print(formatDateTime(date)); 226 } 227 } 228 else 229 /* 230 ** Haven't decided how to handle Types.DECIMAL and 231 ** TYPES.GENERIC. However, this may be academic as I don't 232 ** think that these will occur in the databases 233 ** that I will be using. 234 */ 235 { 236 String working = rs.getString(i); 237 if (rs.wasNull()) 238 { output.print(new String()); } 239 else 240 { output.print(working); } 241 } 242 } 243 catch (Exception e) 244 { throw new java.io.IOException("Error detected in column " + 245 Integer.toString(i) + ":" + 246 e.getClass().getName() + " : " + 247 e.getMessage()); } 248 } 249 try 250 { output.print("\r\n"); } 251 catch (Exception e) 252 { throw new java.io.IOException("Error detected: " + 253 e.getClass().getName() + " : " + 254 e.getMessage()); } 255 } 256 /** 257 * Method of calling formatString when it doesn't 258 * depend on the column in the result set. 259 * @param valueIn String to be processed 260 * @return Processed string 261 */ 262 protected String formatString(String valueIn) 263 { return formatString(valueIn, 1); } 264 /** 265 * Double up on all double quotes in string and surround the string with 266 * double quotes so that it will be in the proper format for the CSV file. 267 * @param valueIn String to be processed 268 * @param column Column number of item (not used unless method overriden) 269 * @return Processed string 270 */ 271 protected String formatString(String valueIn, int column) 272 { 273 String working = null; 274 if (valueIn == null) 275 { working = " "; } 276 else if (valueIn.length() == 0) 277 { working = " "; } 278 else 279 { 280 java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\\\""); 281 java.util.regex.Matcher matcher = pattern.matcher(valueIn); 282 working = matcher.replaceAll("\"\""); 283 } 284 if (working == null) { working = " "; } 285 return "\"" + working + "\""; 286 } 287 /** 288 * Alternate calling sequence, leaving out the column number. 289 * @param valueIn Floating point number to be formatted 290 * @return Formatted string 291 */ 292 protected String formatDouble (double valueIn) 293 { return formatDouble (valueIn, 1); } 294 /** 295 * Formatter for floating point numbers. 296 * <p>This method can be overridden to control printing of 297 * the values in the CSV file.</p> 298 * @param valueIn Floating point number to be processed 299 * @param column Column number 300 * @return Formatted string 301 */ 302 protected String formatDouble (double valueIn, int column) 303 { return Double.toString(valueIn); } 304 /** 305 * Alternate calling sequence, omitting the column number. 306 * @param valueIn Integer value to be formatted 307 * @return Formatted string 308 */ 309 protected String formatInteger (int valueIn) 310 { return formatInteger(valueIn, 1); } 311 /** 312 * Formatter for integer values. 313 * <p>This method can be overridden to control printing of 314 * the values in the CSV file.</p> 315 * @param valueIn Integer to be formatted 316 * @param column Column number 317 * @return Formatted string 318 */ 319 protected String formatInteger (int valueIn, int column) 320 { return Integer.toString(valueIn); } 321 /** 322 * Alternate calling sequence, leaving out the column number 323 * @param valueIn java.util.Date object containing date and time 324 * @return Formatted string 325 */ 326 protected String formatDateTime (java.util.Date valueIn) 327 { return formatDateTime(valueIn, 1); } 328 /** 329 * Formatter for values involving date and time. 330 * <p>This method can be overridden to control printing of 331 * the values in the CSV file.</p> 332 * @param valueIn java.util.Date object containing date and time 333 * @param column Column number 334 * @return Formatted string 335 */ 336 protected String formatDateTime (java.util.Date valueIn, int column) 337 { 338 java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("MM/dd/yyyy HH:mm"); 339 return format.format(valueIn); 340 } 341 }