001package bradleyross.j2ee.servlets;
002import bradleyross.library.helpers.StringHelpers;
003import java.io.Serializable;
004import java.io.IOException;
005import javax.servlet.ServletConfig;
006import javax.servlet.http.HttpServlet;
007import javax.servlet.http.HttpServletRequest;
008import javax.servlet.http.HttpServletResponse;
009import javax.servlet.ServletException;
010import java.sql.Date;
011import java.sql.PreparedStatement;
012import java.sql.ResultSet;
013import java.sql.SQLException;
014import java.sql.Types;
015import java.text.NumberFormat;
016/**
017 * Superclass for servlets.
018 * 
019 * @author Bradley Ross
020 * @see bradleyross.library.helpers.ServletHelpers
021 *
022 */
023public abstract class Servlet extends HttpServlet 
024{
025        /**
026         * Inserted for debugging;
027         */
028        public Servlet()
029        { ; }
030        /**
031         * This class is placed here so that the programmer can subclass either
032         * Servlet.ThisPage or bradleyross.library.servlets.ThisPage.
033         * 
034         * @author Bradley Ross
035         *
036         */
037        public class ThisPage extends bradleyross.j2ee.servlets.ThisPage
038        {
039                /**
040                 * Constructor provided to allow subclassing.
041                 */
042                public ThisPage()
043                { ; }
044                /**
045                 * Initializes the object with information about the HTTP transaction.
046                 * @param request HttpServletRequest object
047                 * @param response HttpServletResponse object
048                 * @param config ServletConfig object
049                 * @throws IOException
050                 */
051                public ThisPage(HttpServletRequest request, HttpServletResponse response, ServletConfig config)
052                throws IOException
053                {
054                        super(request, response, config);
055                }
056        }
057        /**
058         * Added to satisfy Serializable interface.
059         * @see Serializable
060         */
061        private static final long serialVersionUID = 1L;
062        /**
063         * Information on the configuration of the servlet.
064         * 
065         * <p>This is used to pass information from the {@link #init(ServletConfig) } method to the
066         *    other methods in the class.</p>
067         */
068        private ServletConfig config = null;
069        /**
070         * Called when initializing the object for handling HTTP
071         * transactions.
072         * @param configValue Servlet configuration object
073         * @throws ServletException
074         */
075        public void init(ServletConfig configValue) throws ServletException
076        {
077                config = configValue;
078        }
079        /**
080         * Obtains servlet configuration information.
081         * @return servlet configuration object
082         */
083        public ServletConfig getConfig()
084        {
085                return config;
086        }
087        /**
088         * Carries out processing for a HTTP transaction.
089         * 
090         * @see #starter(ThisPage)
091         * @see #processor(ThisPage)
092         * @see #ender(ThisPage)
093         * 
094         * @param requestValue Request object
095         * @param responseValue Response object
096         * @throws IOException
097         */
098        public void service (HttpServletRequest requestValue, HttpServletResponse responseValue)
099        throws IOException
100        {
101                ThisPage thisPage = new ThisPage (requestValue, responseValue, getConfig());
102                starter(thisPage);
103                if (thisPage.getTerminateRequest())
104                { return; }
105                processor(thisPage);
106                if (thisPage.getTerminateRequest())
107                { return; }
108                ender(thisPage);
109                if (thisPage.getTerminateRequest())
110                { return; }
111                String address = thisPage.getRedirectAddress();
112                if (address != null)
113                {
114                        thisPage.getResponse().sendRedirect(thisPage.getResponse().encodeRedirectURL(address));
115                }
116        }
117        /**
118         * Processing before calling processor method.
119         * 
120         * <p>This method can be overridden in subclasses to provide a class
121         *    that carries out the desired operation for a group of classes.</p>
122         * 
123         * @see #processor(ThisPage)
124         * @param thisPage Information on this HTTP transaction
125         * @throws IOException
126         */
127        public void starter(ThisPage thisPage) throws IOException
128        { 
129                if (thisPage != null)
130                {
131                        thisPage.addMessage("Running Servlet.starter(ThisPage)");
132                        thisPage.addMessage("This is a dummy stub that doesn't do any work");
133                }
134        }
135        /**
136         * Processing after calling processor method.
137         * 
138         * <p>This method can be overridden in subclasses to provide a class
139         *    that carries out the desired operation for a group of classes.</p>
140         * 
141         * @see #processor(ThisPage)
142         * @param thisPage Information on this HTTP transaction
143         * @throws IOException
144         */
145        public void ender(ThisPage thisPage) throws IOException
146        { 
147                if (thisPage != null)
148                {
149                        thisPage.addMessage("Running Servlet.ender");
150                        thisPage.addMessage("This is a dummy stub that doesn't do any work");
151                }
152        }
153        /**
154         * Class specific processing for this class.
155         * 
156         * @param thisPage Information on this HTTP transaction
157         * @throws IOException
158         */
159        protected abstract void processor(ThisPage thisPage) throws IOException;
160        /**
161         * Construct an SQL date using the year, month, and day
162         * @param yearValue Year (4 digit)
163         * @param monthValue Number of Month (January = 1)
164         * @param dayValue Day of month
165         * @return SQL date
166         */
167        protected Date buildDate(String yearValue, String monthValue, String dayValue) 
168        {
169                int year = -1;
170                int month = -1;
171                int day = -1;
172                Date working = null;
173                try
174                {
175                year = Integer.parseInt(yearValue.trim());
176                month = Integer.parseInt(monthValue.trim());
177                day = Integer.parseInt(dayValue.trim());
178                working = buildDate(year, month, day);
179                }
180                catch (NumberFormatException e)
181                {
182                        throw new IllegalArgumentException ("buildDate unable to process " + yearValue + ":" + monthValue + ":" + dayValue);
183                }
184                return working;
185        }
186        /**
187         * Construct an SQL date using the year, month, and day
188         * @param year Year (4 digit)
189         * @param month Number of month (January = 1)
190         * @param day Day of month
191         * @return SQL date
192         */
193        protected Date buildDate(int year, int month, int day) 
194        {
195                NumberFormat format2 =  NumberFormat.getIntegerInstance();
196                NumberFormat format4 =  NumberFormat.getIntegerInstance();
197                format2.setMinimumIntegerDigits(2);
198                format2.setGroupingUsed(false);
199                format4.setMinimumIntegerDigits(4);
200                format4.setGroupingUsed(false);
201                return Date.valueOf(format4.format(year) + "-" + format2.format(month) + "-" + format2.format(day));
202        }
203        /**
204         * Obtain date range from HTTP request.
205         * @see Date
206         * @param instance Object containing information for this HTTP request
207         */
208        protected void setDateRange(ThisPage instance)
209        {
210                HttpServletRequest req = instance.getRequest();
211                
212                   /*
213                   * For the constructor for GregorianCalendar, January is
214                   * month 0.
215                   */
216                   Date start = buildDate
217                         (req.getParameter("START_YEAR"),
218                         req.getParameter("START_MONTH"), 
219                         req.getParameter("START_DAY"));
220                   Date end = buildDate
221                         (req.getParameter("END_YEAR"),
222                         req.getParameter("END_MONTH"), 
223                         req.getParameter("END_DAY"));
224                   instance.setStartDate(start);
225                   instance.setEndDate(end);
226        }
227        /**
228         * Build a date in the java.sql.Date escape format.
229         * @param year Year as an integer
230         * @param month Month as an integer from 1 to 12
231         * @param day Day of month as an integer from 1 to 31
232         * @return Date in escape format
233         * @see Date#toString()
234         * <p>It appears that SQL Server is unable to handle dates before the
235         *    year 1900.</p>
236         * @throws SQLException
237         */
238        public static String buildEscapeDate(int year, int month, int day) throws IOException
239        {
240                if (year < 50)
241                {
242                        year = year + 2000;
243                }
244                else if (year < 100)
245                {
246                        year = year + 1900;
247                }
248                if (year < 1900 || year > 3000 || month < 1 || month > 12 || day < 1 || day > 31)
249                {
250                        throw new IOException("Unable to convert year=" + 
251                                        Integer.toString(year) + " month=" +
252                                        Integer.toString(month) + " day=" + Integer.toString(day) + 
253                                        " java.sql.Date escape format");
254                }
255                NumberFormat format2 =  NumberFormat.getIntegerInstance();
256                NumberFormat format4 =  NumberFormat.getIntegerInstance();
257                format2.setMinimumIntegerDigits(2);
258                format2.setGroupingUsed(false);
259                format4.setMinimumIntegerDigits(4);
260                format4.setGroupingUsed(false);
261                return format4.format(year) + "-" + format2.format(month) + "-" + format2.format(day);
262        }
263        /**
264         * Build a date in the java.sql.Date escape format.
265         * @param year String containing the year as an integer
266         * @param month String containing the month as an integer from 1 to 12
267         * @param day String containing the day of month as an integer from 1 to 31
268         * @return Date in escape format
269         * @see Date#toString()
270         * @throws IOException
271         */
272        public static String buildEscapeDate(String year, String month, String day) throws IOException
273        {
274                if (year == null)
275                {
276                        throw new IOException("No parameter for year value");
277                }
278                if (month == null)
279                {
280                        throw new IOException("No parameter for month value");
281                }
282                if (day == null)
283                {
284                        throw new IOException("No parameter for day value");
285                }
286                if (year.trim().length() == 0 && month.trim().length() == 0 && day.trim().length() == 0)
287                {
288                        return "0000-00-00";
289                }
290                int yearValue = -1;
291                int monthValue = -1;
292                int dayValue = -1;
293                try
294                {
295                        yearValue = Integer.parseInt(year.trim());
296                        monthValue = Integer.parseInt(month.trim());
297                        dayValue = Integer.parseInt(day.trim());
298                }
299                catch (Exception e)
300                {
301                        throw new IOException("Unable to convert year=" + 
302                                        year + " month=" +
303                                        month + " day=" + day + 
304                                        " java.sql.Date escape format");
305                }
306                return buildEscapeDate(yearValue, monthValue, dayValue);
307        }
308        /**
309         * Generate an SQL escape date.
310         * @param date SQL date
311         * @return SQL escape date
312         */
313        public static String buildEscapeDate(java.sql.Date date)
314        {
315                if (date == null)
316                {
317                        return "0000-00-00";
318                }
319                else 
320                {
321                        return date.toString();
322                }
323        }
324        /**
325         * Build a date in the java.sql.Date escape format.
326         * @param year Year as an integer
327         * @param month Month as an integer from 1 to 12
328         * @param day Day of month as an integer from 1 to 31
329         * @param defaultDate Date to use if error in processing
330         * @return Date in escape format
331         * @see Date#toString()
332         * <p>It appears that SQL Server is unable to handle dates before the
333         *    year 1900.</p>
334         * @throws SQLException
335         */
336        public static String buildEscapeDate(int year, int month, int day, String defaultDate) throws IOException
337        {
338                if (year < 50)
339                {
340                        year = year + 2000;
341                }
342                else if (year < 100)
343                {
344                        year = year + 1900;
345                }
346                if (year < 1900 || year > 3000 || month < 1 || month > 12 || day < 1 || day > 31)
347                {
348                        return defaultDate;
349                }
350                NumberFormat format2 =  NumberFormat.getIntegerInstance();
351                NumberFormat format4 =  NumberFormat.getIntegerInstance();
352                format2.setMinimumIntegerDigits(2);
353                format2.setGroupingUsed(false);
354                format4.setMinimumIntegerDigits(4);
355                format4.setGroupingUsed(false);
356                return format4.format(year) + "-" + format2.format(month) + "-" + format2.format(day);
357        }
358        /**
359         * Build a date in the java.sql.Date escape format.
360         * @param year String containing the year as an integer
361         * @param month String containing the month as an integer from 1 to 12
362         * @param day String containing the day of month as an integer from 1 to 31
363         * @param defaultDate Date to use if error in processing
364         * @return Date in escape format
365         * @see Date#toString()
366         * @throws IOException
367         */
368        public static String buildEscapeDate(String year, String month, String day, String defaultDate) throws IOException
369        {
370                if (year == null || month == null || day == null)
371                {
372                        return defaultDate;
373                }
374                if (year.trim().length() == 0 || month.trim().length() == 0 || day.trim().length() == 0)
375                {
376                        return defaultDate;
377                }
378                int yearValue = -1;
379                int monthValue = -1;
380                int dayValue = -1;
381                try
382                {
383                        yearValue = Integer.parseInt(year.trim());
384                        monthValue = Integer.parseInt(month.trim());
385                        dayValue = Integer.parseInt(day.trim());
386                }
387                catch (Exception e)
388                {
389                        return defaultDate;
390                }
391                return buildEscapeDate(yearValue, monthValue, dayValue);
392        }
393        /**
394         * Break a java.sql.Date escape format date into a set of
395         * string objects for the year, month, and day portions.
396         * @see Date#toString()
397         * @param escapeDate Date to be parsed
398         * @return Array of strings
399         */
400        public static String[] parseEscapeDate(String escapeDate)
401        {
402                String working[] = new String[3];
403                if (escapeDate == null)
404                {
405                        working[0] = new String();
406                        working[1] = new String();
407                        working[2] = new String();
408                }
409                else if (escapeDate.equals("0000-00-00"))
410                {
411                        working[0] = " value=\" \" ";
412                        working[1] = " value=\" \" ";
413                        working[2] = " value=\" \" ";
414                }
415                
416                else if (escapeDate.trim().length() < 10)
417                {
418                        working[0] = new String();
419                        working[1] = new String();
420                        working[2] = new String();
421                }
422                else
423                {
424                        working[0] = " value=\"" + escapeDate.substring(0, 4) + "\" ";
425                        working[1] = " value=\"" + escapeDate.substring(5, 7) + "\" ";
426                        working[2] = " value=\"" + escapeDate.substring(8) + "\" ";
427                }       
428                return working;
429        }
430
431        
432        
433        /**
434         * Utility method for setting parameters in Prepared Statements.
435         * <p>For CHAR data types.</p>
436         * @param stmt PreparedStatement object
437         * @param location Number of parameter
438         * @param value Value to be loaded in parameter
439         * @throws SQLException
440         */
441        protected void loadChar(PreparedStatement stmt, int location, String value) throws SQLException
442        {
443                if (stmt == null)
444                {
445                        throw new SQLException("PreparedStatement object has null value");
446                }
447                if (location < 0)
448                {
449                        throw new SQLException("Negative number for parameter number in prepared statement");
450                }
451                if (value == null)
452                {
453                        stmt.setNull(location, Types.CHAR );
454                }
455                else if (value.trim().length() == 0)
456                {
457                        stmt.setNull(location, Types.CHAR);
458                }
459                else
460                {
461                        stmt.setString(location, value);
462                }
463        }
464        /**
465         * Utility method for setting parameters in Prepared Statements.
466         * <p>For VARCHAR data types.</p>
467         * @param stmt PreparedStatement object
468         * @param location Number of parameter
469         * @param value Value to be loaded in parameter
470         * @throws SQLException
471         */
472        protected void loadVarchar(PreparedStatement stmt, int location, String value) throws SQLException
473        {
474                if (value == null)
475                {
476                        stmt.setNull(location, Types.VARCHAR );
477                }
478                else if (value.trim().length() == 0)
479                {
480                        stmt.setNull(location, Types.VARCHAR);
481                }
482                else
483                {
484                        stmt.setString(location, value);
485                }
486        }
487        /**
488         * Utility to aid in creating option lists for pull-down
489         * menus.
490         * @param value1 First string to be compared
491         * @param value2 Second string to be compared
492         * @return The word SELECTED if the two strings match, otherwise
493         *         a blank.
494         */
495        public static String matchValues(String value1, String value2)
496        {
497                if (value1 == null || value2 == null)
498                {
499                        return " ";
500                }
501                else if (value1.equalsIgnoreCase(value2))
502                {
503                        return " selected=\"selected\" ";
504                }
505                else
506                {
507                        return " ";
508                }
509        }
510        /**
511         * Obtains a value from a result set for use as the value attribute of a
512         * input element of type text.
513         * @param rs Result set
514         * @param name Name of column in result set
515         * @return Text string for value attribute.
516         */
517        public static String getTextValue(ResultSet rs, String name)
518        {
519                try {
520                        String working = rs.getString(name);
521                        if (rs.wasNull())
522                        {       return new String();    }
523                        else if (working.length() == 0)
524                        {       return new String();    }
525                        else 
526                        { return "value=\"" + StringHelpers.escapeHTML(working) + "\""; }
527                } catch (SQLException e)
528                { return new String();  }
529        }
530        /**
531         * Obtains a value from a result set for use as the starting value of a
532         * Textarea element.
533         * @see StringHelpers#escapeHTML(String, String)
534         * @param rs Result set
535         * @param name Name of column in result set
536         * @return Text string for Textarea tag.
537         */
538        public static String getTextareaValue(ResultSet rs, String name)
539        {
540                try 
541                {
542                        String working = rs.getString(name);
543                        if (rs.wasNull())
544                        { return new String(); }
545                        else if (working.trim().length() == 0)
546                        {  return new String(); }
547                        else 
548                        { return StringHelpers.escapeHTML(working, "TEXTAREA"); }
549                } 
550                catch (SQLException e)
551                { return new String(); }
552        }
553        /**
554         * Returns the string for a non-breaking space if a string
555         * column in a table is null or blank, otherwise returns an
556         * escaped string.
557         * @param rs Result set containing value
558         * @param name Name of column
559         * @return Processed string for insertion in web page
560         */
561        public static String getString(ResultSet rs, String name)
562        {
563                try
564                {
565                        String working = rs.getString(name);
566                        if (rs.wasNull())
567                        { return "&nbsp;"; }
568                        else if (working.trim().length() == 0)
569                        { return "&nbsp;"; }
570                        else
571                        { return StringHelpers.escapeHTML(working); }
572                }
573                catch (SQLException e)
574                {
575                        return new String(); 
576                }
577        }
578        /**
579         * Internal debugging aid for checking parseEscapeDate.
580         * @param value String to be parsed
581         * @see #parseEscapeDate(String)
582         */
583        protected static void innerTest(String value)
584        {
585                String test[] = parseEscapeDate(value);
586                System.out.print(value);
587                for (int i = 0; i < test.length; i++)
588                {
589                        System.out.print(" " + test[i]);
590                }
591                System.out.println();
592        }
593        /**
594         * This was inserted for testing of some of the internal static methods
595         * and is only for debugging purposes.
596         * 
597         * @param args Not used
598         */
599        public static void main(String args[])
600        {
601                String test[] = { "2009-09-09", "0000-00-00"};
602                for (int i = 0; i < test.length; i++)
603                { innerTest(test[i]); }
604        }
605}