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}