001package bradleyross.j2ee.servlets; 002import java.io.File; 003import java.io.IOException; 004import java.io.ByteArrayOutputStream; 005import java.io.FileWriter; 006import java.io.StringWriter; 007import java.io.PrintWriter; 008import java.util.Enumeration; 009import java.util.Date; 010import java.net.URLConnection; 011import javax.servlet.ServletConfig; 012import javax.servlet.ServletContext; 013import javax.servlet.ServletException; 014import javax.servlet.http.HttpServlet; 015import javax.servlet.http.HttpServletRequest; 016import javax.servlet.http.HttpServletResponse; 017import bradleyross.library.helpers.FileHelpers; 018/** 019 * Servlet that will return the contents of a web page that is 020 * contained on a different web site (under consideration). 021 * 022 * This is a proposed class that has not yet been implemented. 023 * 024 * <ul> 025 * <li>The name of the file to be retrieved is obtained 026 * using {@link HttpServletRequest#getPathInfo()}.</li> 027 * <li>The MIME type is obtained from the file name using 028 * {@link URLConnection#guessContentTypeFromName(String)}</li> 029 * <li>The name of the INIT parameter containing the document 030 * root is given by the parameter <code>DocumentRootParameter</code>. 031 * If the parameter is not specified for the servlet, the default 032 * name is <code>DocumentRoot</code>.</li> 033 * <li>If the INIT parameter <code>LogFile</code> has been set, it 034 * is used as the location for a log file for the servlet.</li> 035 * </ul> 036 * <p>Consideration should be given to increasing security for the 037 * servlet. The first step would be to stop file names from 038 * containing two periods in a row.</p> 039 * @author Bradley Ross 040 * 041 */ 042public class GetWebPage extends HttpServlet 043{ 044 /** 045 * ID number to satisfy SERIALIZABLE interface. 046 */ 047 private static final long serialVersionUID = 1L; 048 /** 049 * Keeps track of how many instances of this servlet have 050 * been opened by the application server. 051 * 052 * <p>There is a problem in that Tomcat does not appear to 053 * treating this as a static value. The idea was that 054 * the application server could run multiple GetFile 055 * objects and would use this parameter to set the 056 * value of instanceNumber so that log files for the 057 * different objects would go to different files. Instead, 058 * the value appears to be zero each time the init 059 * method is called. The init method should only be called 060 * once for each GetFile object.</p> 061 */ 062 protected static int runningInstanceNumber = 0; 063 /** 064 * Identifies this instance of the servlet. 065 * 066 * <p>It was intended that this variable would identify the 067 * GetFile object if the application server was running 068 * multiple instances of the GetFile class.</p> 069 */ 070 protected int instanceNumber = 0; 071 /** 072 * Keeps track of the different instances of the service method that are running. 073 */ 074 protected int runningServiceNumber = 0; 075 /** 076 * ServletConfig object as passed to the init method. 077 */ 078 protected ServletConfig config = null; 079 /** 080 * ServletContext object containing information 081 * for the entire web application. 082 */ 083 protected ServletContext context = null; 084 /** 085 * Name of parameter containing the location of the document root. 086 */ 087 protected String fileRootParameter = "DocumentRoot"; 088 /** 089 * Name of root directory for reading files. 090 */ 091 protected String fileRoot = null; 092 /** 093 * Name of log file. 094 */ 095 protected String logFile = null; 096 /** 097 * If true, place messages in log file. 098 */ 099 protected boolean logFileActive = false; 100 /** 101 * Character string indicating end of line. 102 */ 103 protected String lineSeparator = null; 104 /** 105 * Servlet has a valid configuration when true. 106 */ 107 protected boolean valid = true; 108 /** 109 * Run when object is created for processing calls to servlet. 110 * 111 * <p>The application server can create 112 * multiple objects for a single servlet to improve 113 * performance.</p> 114 */ 115 public void init(ServletConfig configIn) throws ServletException 116 { 117 super.init(configIn); 118 runningInstanceNumber++; 119 instanceNumber = runningInstanceNumber; 120 config = configIn; 121 context = config.getServletContext(); 122 try 123 { 124 lineSeparator = System.getProperty("line.separator"); 125 } 126 catch (Exception e) 127 { 128 lineSeparator = "\n"; 129 } 130 String working = (String) null; 131 working = config.getInitParameter("DocumentRootParameter"); 132 if (working != null) 133 { fileRootParameter = working; } 134 else 135 { fileRootParameter = "DocumentRoot"; } 136 working = (String) null; 137 working = config.getInitParameter(fileRootParameter); 138 if (working != null) 139 { fileRoot = working; } 140 else 141 { 142 working = null; 143 working = context.getInitParameter(fileRootParameter); 144 if (working != null) 145 { 146 fileRoot = working; 147 } 148 } 149 if (fileRoot == null) 150 { valid = false; } 151 working = (String) null; 152 working = config.getInitParameter("LogFile"); 153 if (working == null) 154 { 155 logFileActive = false; 156 } 157 else 158 { 159 logFile = config.getInitParameter("LogFile") + Integer.toString(instanceNumber) + ".txt"; 160 runningServiceNumber = 0; 161 logFileActive = true; 162 createLogEntry("Instance of servlet has been created"); 163 } 164 } 165 /** 166 * Run when object for processing servlet is no longer needed 167 * and is destroyed. 168 */ 169 public void destroy() 170 { 171 createLogEntry("Instance has been destroyed"); 172 super.destroy(); 173 } 174 protected void createLogEntry(String message) 175 { 176 if (!logFileActive) 177 { 178 return; 179 } 180 boolean success = false; 181 for (int i = 0; i < 3; i++) 182 { 183 try 184 { 185 FileWriter output = new FileWriter(logFile, true); 186 output.write(new Date().toString() + " "); 187 output.write(message); 188 output.write(lineSeparator); 189 output.close(); 190 success = true; 191 } 192 catch (IOException e) 193 { 194 success = false; 195 } 196 if (success) { break; } 197 } 198 } 199 protected void createLogEntry(String message, int serviceNumber) 200 { 201 if (!logFileActive) 202 { 203 return; 204 } 205 boolean success = false; 206 for (int i = 0; i < 3; i++) 207 { 208 try 209 { 210 FileWriter output = new FileWriter(logFile, true); 211 output.write(new Date().toString() + " " + 212 Integer.toString(serviceNumber) + " " + 213 message + lineSeparator); 214 output.close(); 215 success = true; 216 } 217 catch (IOException e) 218 { 219 success = false; 220 } 221 if (success) { break; } 222 } 223 } 224 protected void createLogEntry(String message, Throwable e, int serviceNumber) 225 { 226 if (!logFileActive) 227 { 228 return; 229 } 230 boolean success = false; 231 for (int i = 0; i < 3; i++) 232 { 233 try 234 { 235 FileWriter output = new FileWriter(logFile, true); 236 StringWriter working = new StringWriter(); 237 PrintWriter writer = new PrintWriter(working); 238 e.printStackTrace(writer); 239 output.write(new Date().toString() + " " + Integer.toString(serviceNumber) + 240 " " + message + lineSeparator + working.toString()); 241 output.close(); 242 success = true; 243 } 244 catch (IOException e1) 245 { 246 success = false; 247 } 248 if (success) { break; } 249 } 250 } 251 252 /** 253 * Processes request to a servlet. 254 * 255 * <p>There can be many instances of the service method running at 256 * the same time. This must be taken into account when coding this 257 * method.</p> 258 * 259 * <p>It may be necessary to change this file so that the information 260 * is recorded to a byte array and then repeated as necessary until 261 * success is achieved.</p> 262 * 263 * @param req Object containing request information 264 * @param res Object containing response information 265 * @see HttpServletRequest 266 * @see HttpServletResponse 267 * @see IOException 268 */ 269 public void service (HttpServletRequest req, 270 HttpServletResponse res) throws IOException 271 { 272 if (!valid) 273 { 274 res.sendError(500, "Invalid initialization parameters for servlet"); 275 } 276 runningServiceNumber++; 277 if (runningServiceNumber > 9999) {runningServiceNumber = 0; } 278 int serviceNumber = runningServiceNumber; 279 /** 280 * Object representing the output to the HTTP response when 281 * processing a text file. 282 */ 283 java.io.PrintWriter output = null; 284 /** 285 * Object representing the output to the HTTP response when 286 * processing a binary file. 287 */ 288 java.io.OutputStream stream = null; 289 String fullFileName = null; 290 String suffix = null; 291 String contextPath = null; 292 String servletPath = null; 293 String pathInfo = null; 294 String queryString = null; 295 String mimeType = null; 296 try 297 { 298 contextPath = req.getContextPath(); 299 pathInfo = req.getPathInfo(); 300 servletPath = req.getServletPath(); 301 queryString = req.getQueryString(); 302 if (pathInfo == null) 303 { 304 res.sendError(500, "No path info in request"); 305 return; 306 } 307 mimeType = URLConnection.guessContentTypeFromName(pathInfo); 308 createLogEntry("Running GetFile servlet for " + pathInfo + " with MIME type of " + 309 mimeType, serviceNumber); 310 311 312 if ((System.getProperty("file.separator")).equals("\\")) 313 { 314 pathInfo = pathInfo.replaceAll("/", "\\\\"); 315 } 316 fullFileName = fileRoot.concat(pathInfo); 317 int position = pathInfo.lastIndexOf("."); 318 if (position >= 0 && position < (pathInfo.length() - 1)) 319 { 320 suffix = pathInfo.substring(position + 1).toUpperCase(); 321 } 322 if (queryString == null) 323 { ; } 324 else if (queryString.toUpperCase().startsWith("QUERY")) 325 { 326 Enumeration<String> initParameterNames = config.getInitParameterNames(); 327 Enumeration<String> parameterNames = initParameterNames; 328 output = res.getWriter(); 329 res.setContentType("text/plain"); 330 output.println("context path: " + contextPath); 331 output.println("servlet path: " + servletPath); 332 output.println("path info: " + pathInfo ); 333 output.println("query string: " + queryString); 334 while (parameterNames.hasMoreElements()) 335 { 336 String name = parameterNames.nextElement(); 337 output.println(name + " : " + config.getInitParameter(name)); 338 } 339 output.println("Suffix: " + suffix); 340 output.println("Full file name: " + fullFileName); 341 File tester = new File(fullFileName); 342 output.println("***"); 343 output.println("Does file exist: " + Boolean.toString(tester.exists())); 344 if (tester.exists()) 345 { output.println("File exists"); } 346 else 347 { output.println("File does not exist"); } 348 output.println("***"); 349 output.flush(); 350 return; 351 } 352 if (!(new File(fullFileName)).exists()) 353 { 354 /* 355 * If file does not exist, retry a second later. 356 */ 357 boolean success = false; 358 for (int i = 0; i < 3; i++) 359 { 360 try 361 { 362 Thread.sleep(1000l * (long) (i + 1)); 363 } 364 catch (InterruptedException e) 365 { ; } 366 if (new File(fullFileName).exists()) 367 { 368 success = true; 369 break; 370 } 371 else 372 { 373 createLogEntry("File " + fullFileName + " did not exist", serviceNumber); 374 } 375 } 376 if (!success) 377 { 378 createLogEntry("File " + fullFileName + " does not exist: Aborting", serviceNumber); 379 res.sendError(500, "File " + fullFileName + " does not exist"); 380 return; 381 } 382 } 383 if (suffix == null) 384 { 385 createLogEntry("No suffix found"); 386 res.sendError(500, "No suffix found"); 387 return; 388 } 389 else if (suffix.equals("TXT")) 390 { 391 output = res.getWriter(); 392 res.setContentType("text/plain"); 393 output.print(FileHelpers.readTextFile(fullFileName)); 394 return; 395 } 396 else if (suffix.equalsIgnoreCase("PNG") || 397 suffix.equalsIgnoreCase("JPG") || 398 suffix.equalsIgnoreCase("JPEG") || 399 suffix.equalsIgnoreCase("GIF") ) 400 { 401 stream = res.getOutputStream(); 402 res.setContentType(mimeType); 403 ByteArrayOutputStream byteArray = new ByteArrayOutputStream(); 404 byte[] createdArray = null; 405 boolean success = false; 406 for (int trial = 0; trial < 5; trial++) 407 { 408 try 409 { 410 File newOutputFile = new File(fullFileName); 411 FileHelpers.getBytes( 412 newOutputFile, 413 byteArray); 414 createdArray = byteArray.toByteArray(); 415 createLogEntry("Length of " + fullFileName + " is " + 416 Integer.toString(createdArray.length), serviceNumber); 417 res.setContentLength(createdArray.length); 418 byteArray.writeTo(stream); 419 success = true; 420 } 421 catch (Exception e) 422 { 423 success = false; 424 byteArray.reset(); 425 if (trial < 4) 426 { 427 createLogEntry(new Date().toString() + 428 " Warning: error in GetFile.service reading " + fullFileName + " - " + 429 e.getClass().getName() + " " + e.getMessage() + " - " + 430 "Retrying " + Integer.toString(trial), e, serviceNumber); 431 } 432 else 433 { 434 createLogEntry(new Date().toString() + 435 " Warning: error in GetFile.service reading " + fullFileName + " - " + 436 e.getClass().getName() + " " + e.getMessage() + " - " + 437 "Aborting", e, serviceNumber); 438 throw new IOException(e.getClass().getName() + " " + e.getMessage()); 439 } 440 } 441 if (success) 442 { 443 createLogEntry("Processing of " + fullFileName + " complete", serviceNumber); 444 break; 445 } 446 } 447 if (!success) 448 { 449 createLogEntry("Failure in processing " + fullFileName, serviceNumber); 450 res.sendError(500, "Unable to read" + fullFileName); 451 } 452 return; 453 } 454 else 455 { 456 output = res.getWriter(); 457 res.setContentType("text/plain"); 458 output.println("Suffix " + suffix + 459 " does not have a handler defined"); 460 createLogEntry ("File " + fullFileName + 461 " does not have a handler defined", serviceNumber); 462 res.sendError(500, "File " + fullFileName + 463 " does not have a handler defined"); 464 } 465 } 466 catch (java.io.IOException e) 467 { 468 createLogEntry("Failure in servlet: " + 469 e.getClass().getName() + " " + 470 e.getMessage(), e, serviceNumber); 471 res.sendError(500, "Internal error in servlet: " + e.getClass().getName() + " " + 472 e.getMessage()); 473 } 474 } 475 /* End of service method */ 476 /** 477 * This provides an error message if it is attempted to run this 478 * class as a Java application rather than a Java servlet. 479 * 480 * @param args Not used at this time 481 */ 482 public static void main(String[] args) 483 { 484 System.out.println("No main program provided."); 485 } 486}