001package bradleyross.j2ee.servlets;
002import bradleyross.library.database.DatabaseProperties;
003import bradleyross.library.helpers.GenericPrinter;
004import bradleyross.library.helpers.StringHelpers;
005import java.io.IOException;
006import java.io.PrintWriter;
007import java.io.StringWriter;
008import java.sql.SQLException;
009import java.sql.ResultSet;
010import java.sql.ResultSetMetaData;
011import java.sql.DatabaseMetaData;
012import java.sql.Types;
013import java.sql.Driver;
014import java.util.Vector;
015import javax.servlet.ServletConfig;
016import javax.servlet.ServletException;
017import javax.servlet.http.HttpServlet;
018import javax.servlet.http.HttpServletRequest;
019import javax.servlet.http.HttpServletResponse;
020import org.slf4j.Logger;
021import org.slf4j.LoggerFactory;
022import bradleyross.library.helpers.ExceptionHelper;
023/**
024 * Servlet for showing database schema information using
025 *  {@link DatabaseMetaData}.
026 * 
027 * <p>This was an earlier attempt to do an autodocumenter for
028 *    database schemas.  There was a later version that also listed indices
029 *    and foreign keys and used better formatting, and I'm trying to locate it.</p>
030 * 
031 * <p>It should be noted that SQL Server does not have a BOOLEAN
032 *    type but uses an integer value instead for result sets
033 *    containing boolean data.  A value of 0 indicates false
034 *    while a value of 1 indicates true.<p>
035 * 
036 * <p>The servlet uses the init parameters class, system, and account.</p>
037 * <ul>
038 * <li>class is the name of the class that is a subclass of 
039 *     {@link DatabaseProperties} that serves to connect the databases.</li>
040 * <li>system is the identifier for the database to be used.</li>
041 * <li>account is the name of the account to be used.</li>
042 * </ul>
043 * 
044 * @author Bradley Ross
045 * @see DatabaseMetaData#getCrossReference(String, String, String, String, String, String)
046 * @see DatabaseMetaData#getIndexInfo(String, String, String, boolean, boolean)
047 * @see DatabaseMetaData#getTables(String, String, String, String[])
048 * @see DatabaseMetaData#getImportedKeys(String, String, String)
049 * @see DatabaseMetaData#getExportedKeys(String, String, String)
050 */
051@SuppressWarnings("serial")
052public class ShowSchema extends HttpServlet 
053{
054        Logger log = LoggerFactory.getLogger(this.getClass());
055        ExceptionHelper logger = new ExceptionHelper(log);
056        /**
057         * True if servlet was correctly instantiated.
058         */
059        protected boolean isValid = true;
060        /**
061         * Name of class for handling database.
062         */
063        protected String databaseClassName = null;
064        /**
065         * Class for handling database.
066         */
067        protected Class<?> databaseClass = null;
068        /**
069         * Name of system for database connection.
070         */
071        protected String databaseSystem = null;
072        /**
073         * Description of database.
074         */
075        protected String databaseDescription = null;
076        /**
077         * Name of account to be used for database connection.
078         */
079        protected String databaseAccount = null;
080        /**
081         * List of warning or error messages generated by this application.
082         */
083        protected Vector<String> errorMessages = new Vector<String>();
084        /**
085         * List of informative messages generated by this application.
086         */
087        protected Vector<String> infoMessages = new Vector<String>();
088        /**
089         * Generates an error message for the application.
090         * @param title Text to appear in title
091         * @param message Text to appear in body of message
092         * @return HTML page
093         */
094        public String buildErrorMessage(String title, String message)
095        {
096                StringWriter writer = new StringWriter();
097                GenericPrinter output = new GenericPrinter(writer);
098                output.println("<html><head>");
099                output.println("<title>" + StringHelpers.escapeHTML(title) + "</title>");
100                output.println("</head><body");
101                output.println("<h1>" + StringHelpers.escapeHTML(title) + "</h1>");
102                output.println("<p>" + StringHelpers.escapeHTML(message) + "</p>");
103                if (errorMessages.size() > 0)
104                {
105                        output.println("<h2>Possible Problems</h2>");
106                        output.println("<ul>");
107                        for (int i = 0; i < errorMessages.size(); i++)
108                        {
109                                output.println("<li>" + StringHelpers.escapeHTML(errorMessages.elementAt(i)) 
110                                                + "</li>");
111                        }
112                        output.println("</ul");
113                }
114                if (infoMessages.size() > 0)
115                {
116                        output.println("<h2>Messages</h2>");
117                        output.println("<ul>");
118                        for (int i = 0; i < infoMessages.size(); i++)
119                        {
120                                output.println("<li>" + StringHelpers.escapeHTML(infoMessages.elementAt(i)) 
121                                                + "</li>");
122                        }
123                        output.println("</ul>");
124                }
125                output.println("</body></html>");
126                String result = writer.toString();
127                output.close();
128                return result;
129        }
130
131        /**
132         * Determines whether an item in the database is to be included in the
133         * reports.
134         * 
135         * <p>When dealing with SQLServer, dbo.dtproperties was showing up on the list
136         *    of tables even though it was a system table.  The other system tables
137         *    weren't appearing.</p>
138         * 
139         * @param data Object describing data connection
140         * @param catalog Name of catalog for item in database
141         * @param schema Name of schema for item in database
142         * @param item Name of item in database
143         * @return True if item is to be included in report
144         */
145        protected boolean isIncluded(DatabaseProperties data, String catalog, String schema, String item)
146        {
147                String database = data.getDbms();
148                if (database == null)
149                {
150                        return true;
151                }
152                else if (database.equalsIgnoreCase("sqlserver"))
153                {
154                        try
155                        {
156                                String schemaTest = null;
157                                if (schema == null)
158                                {
159                                        schemaTest = new String();
160                                }
161                                else
162                                {
163                                        schemaTest = schema;
164                                }
165                                if (schemaTest.equalsIgnoreCase("dbo") && item.equalsIgnoreCase("dtproperties"))
166                                {
167                                        return false;
168                                }
169                                else if (schemaTest.equalsIgnoreCase("sys"))
170                                {
171                                        return false;
172                                }
173                                else if (schemaTest.equalsIgnoreCase("INFORMATION_SCHEMA"))
174                                {
175                                        return false;
176                                }
177                                else
178                                {
179                                        return true;
180                                }
181                        }
182                        catch (Exception e)
183                        {
184                                return true;
185                        }
186                }
187                else
188                {
189                        return true;
190                }
191        }
192        /**
193         * Default constructor.
194         * 
195         * <p>The default constructor is used by the Tomcat application
196         *    server when setting up the servlet.  The default 
197         *    constructor must be explicitly defined since there
198         *    is an explicit constructor for the class.</p>
199         */
200        public ShowSchema()
201        { ; }
202        /**
203         * Constructor to allow the methods to be used by a stand-alone
204         * application.
205         * @param className Name of class for defining database connections.
206         * @param system Database system for which connection is to be made.
207         * @param account Account name to be used in connecting to the
208         *        database.
209         */
210        public ShowSchema(String className, String system, String account)
211        {
212                isValid = true;
213                databaseClassName = className;
214                databaseSystem = system;
215                databaseAccount = account;
216                try
217                {
218                        databaseClass = Class.forName(className);
219                }
220                catch (ClassNotFoundException e)
221                {
222                        errorMessages.add("Unable to find class " + className);
223                        isValid = false;
224                        return;
225                }
226                if (!DatabaseProperties.class.isAssignableFrom(databaseClass))
227                {
228                        errorMessages.add("Class " + className + " is not subclass of DatabaseProperties");
229                        isValid = false;
230                        return;
231                }
232                infoMessages.add("Initialization with ShowSchema complete: " +
233                                className + ", " + system + ", " + account);
234        }
235        /**
236         * Obtain a connection to the database based on the initialization
237         * parameters for the servlet.
238         * 
239         * @see #databaseAccount
240         * @see #databaseSystem
241         * @see #databaseClassName
242         * @return Database connection information
243         */
244        protected DatabaseProperties getData()
245        {
246                DatabaseProperties data = null;
247                if (!isValid)
248                {
249                        return null;
250                }
251                try 
252                {
253                        data = (DatabaseProperties) databaseClass.newInstance();
254                } 
255                catch (InstantiationException e) 
256                {
257                        errorMessages.add("Error while instantiating DatabaseProperties object");
258                        errorMessages.add(e.getClass().getName() + " " + e.getMessage());
259                        isValid = false;
260                        return null;
261                } 
262                catch (IllegalAccessException e) 
263                {
264                        errorMessages.add("Error while instantiating DatabaseProperties object");
265                        errorMessages.add(e.getClass().getName() + " " + e.getMessage());
266                        isValid = false;
267                        return null;
268                }
269                data.setDatabaseInstance(databaseSystem, databaseAccount);
270                try 
271                {
272                        data.connect();
273                } 
274                catch (SQLException e) 
275                {
276                        errorMessages.add("Error while opening database connection");
277                        errorMessages.add(data.showAttributes());
278                        errorMessages.add(e.getClass().getName() + " " + e.getMessage());
279                        isValid = false;
280                }
281                if (!isValid)
282                {
283                        return null;
284                }
285                infoMessages.add("Database connection has been opened");
286                return data;
287        }
288        /**
289         * Initializes object for use with servlets.
290         * <p>The following servlet configuration items are used.</p>
291         * <ul>
292         * <li>class - Subclass of DatabaseProperties that is used to connect with the
293         *             database.</li>
294         * <li>system</li>
295         * <li>account</li>
296         * </ul>
297         * @param configIn Servlet configuration object
298         * 
299         */
300        public void init(ServletConfig configIn) throws ServletException
301        { 
302                isValid = true;
303                try
304                {
305                        databaseClassName = configIn.getInitParameter("class");
306                        databaseSystem = configIn.getInitParameter("system");
307                        databaseAccount = configIn.getInitParameter("account");
308                        databaseClass =  Class.forName(databaseClassName);
309                }
310                catch (ClassNotFoundException e)
311                {
312                        isValid = false;
313                        return;
314                }
315                if (!DatabaseProperties.class.isAssignableFrom(databaseClass))
316                {
317                        errorMessages.add("Class " + databaseClassName + " is not subclass of DatabaseProperties");
318                        isValid = false;
319                        return;
320                }
321        }
322        /** 
323         * Place error message on web page if exception is encountered.
324         * 
325         * @param e Exception
326         * @param output Output device for servlet writer
327         */
328        protected void ProblemFound (Exception e, GenericPrinter output, String message)
329        {
330                output.println("<html><head>");
331                output.println("<title>Error encountered in processing database</title>");
332                output.println("</head><body>");
333                output.println("<h1>Problem encountered in connecting to database</h1>");
334                output.println("<p>" + StringHelpers.escapeHTML(message) + "</p>");
335                output.println("<p>System name is " + databaseSystem + "</p>");
336                output.println("<p>Account name is " + databaseAccount + "</p>");
337                output.println("<p>" + StringHelpers.escapeHTML(e.getClass().getName()) + " " + 
338                                StringHelpers.escapeHTML(e.getMessage()) + "</p>");
339                StringWriter sw = new StringWriter();
340                PrintWriter pw = new PrintWriter(sw);
341                e.printStackTrace(pw);
342                output.println("<p>" + StringHelpers.escapeHTML(sw.toString()) + "</p>");
343                output.println("</body></html>");
344        }       
345        /**
346         * This is the method that is executed each time a request is made for the
347         * web page.
348         * 
349         * @param req Request information
350         * @param res Response information
351         * @throws IOException
352         */
353        public void service (HttpServletRequest req,
354                        HttpServletResponse res) throws IOException
355                        {
356                // String rootName = req.getContextPath() + req.getServletPath();
357                DatabaseProperties data = null;
358                PrintWriter writer = null;
359                GenericPrinter generic = null;
360                String pathInfo = null;
361                res.setContentType("text/html");
362                writer = res.getWriter();
363                generic = new GenericPrinter(writer);
364                pathInfo = req.getPathInfo();
365
366                try
367                {
368                        data = (DatabaseProperties) databaseClass.newInstance();
369                        data.setDatabaseInstance(databaseSystem, databaseAccount);      
370                        data.connect();
371                        databaseDescription = data.getSystemDescription();
372                        if (databaseDescription == null)
373                        {
374                                databaseDescription = new String();
375                        }
376                }
377                catch (InstantiationException e)
378                {
379                        logger.error("Problem setting up database connection: ", e);
380                        ProblemFound(e, generic, "Problem setting up database connection: " +
381                                        "InstantiationException");
382                        return;
383                }
384                catch (IllegalAccessException e)
385                {
386                        ProblemFound(e, generic, "Problem setting up database connection: " +
387                                        "IllegalAccessException");
388                        return;
389                }
390                catch (SQLException e)
391                {
392                        ProblemFound(e, generic, "Problem setting up database connection: " +
393                                        "SQLException");
394                        return;
395                }
396                /*
397                 * Now that the page is open, carry out the appropriate action for
398                 * writing the web page.
399                 */
400                if (pathInfo == null || pathInfo.length() == 0 || pathInfo.equals("/") ||
401                                pathInfo.equalsIgnoreCase("/main.do"))
402                {
403                        // generic.print(buildRootPage(data, rootName, debugLevel));
404                        processRootPage(data, generic, req, res);
405                }
406                else if (pathInfo.equalsIgnoreCase("/listtables") ||
407                                pathInfo.equalsIgnoreCase("/listtables.do"))
408                {
409                        processListTables(data, generic, req, res);
410                }
411                else if (pathInfo.equalsIgnoreCase("/listviews") ||
412                                pathInfo.equalsIgnoreCase("/listviews.do"))
413                {
414                        processListViews(data, generic, req, res);
415                }
416                else if (pathInfo.equalsIgnoreCase("/listprocedures") ||
417                                pathInfo.equalsIgnoreCase("/listprocedures.do"))
418                {
419                        processListProcedures(data, generic, req, res);
420                }
421                else if (pathInfo.equalsIgnoreCase("/databaseinformation")||
422                                pathInfo.equalsIgnoreCase("/databaseinformation.do"))
423                {
424                        processDatabaseInformation(data, generic, req, res);
425                }
426                else if (pathInfo.equalsIgnoreCase("/listudts") ||
427                                pathInfo.equalsIgnoreCase("/listudts.do"))
428                {
429                        processUDTInformation(data, generic, req, res);
430                }
431                else if (pathInfo.equalsIgnoreCase("/describetable") ||
432                                pathInfo.equalsIgnoreCase("/describetable.do"))
433                {
434                        processDescribeTable(data, generic, req, res);
435                }
436                else
437                {
438                        //                      generic.print(buildRootPage(data, rootName, debugLevel));
439                        processRootPage(data,generic, req, res);                        
440                }                               
441                /*
442                 * Close the database now that the page is complete.
443                 */
444                try
445                {
446                        data.close();
447                }
448                catch (SQLException e)
449                {
450                        writer.println("<p>Problem closing database connection</p>");
451                        writer.println("<p>" + 
452                                        StringHelpers.escapeHTML(e.getClass().getName() + " " + e.getMessage()) +
453                                        "</p>");
454                }
455                writer.println("</body></html>");
456                        }
457        /**
458         * Generate the root page for schema information for the database.
459         * @param data Object describing database connection
460         * @param output Object for writing web page
461         * @param req HTTP request information
462         * @param res HTTP response information
463         * @throws IOException
464         */
465        protected void processRootPage(DatabaseProperties data, GenericPrinter output,
466                        HttpServletRequest req, HttpServletResponse res) throws IOException
467                        {
468                String rootName = req.getContextPath() + req.getServletPath();
469                output.println("<html><head>");
470                output.println("<title>Database Information</title>");
471                output.println("</head><body>");
472                output.println("<p>The root URL is " + rootName + "</p>");
473                output.println("<h1>Database Schema</h1>");
474                output.println("<ul>");
475                output.println("<li><a href=\"" + rootName + "/DatabaseInformation\">Database Information</a></li>");
476                output.println("<li><a href=\"" + rootName + "/ListTables\">List Tables</a></li>");
477                output.println("<li><a href=\"" + rootName + "/ListViews\">List Views</a></li>");
478                output.println("<li><a href=\"" + rootName + "/ListProcedures\">List Procedures</a></li>");
479                output.println("<li><a href=\"" + rootName + "/ListUDTs\">List User Defined Types (UDT)</a></li>");
480                output.println("</ul>");
481                output.println("<p>DBMS is " + data.getDbms());
482                output.println("<p>System is " + data.getSystem() + "</p>");
483                output.println("<p>" + StringHelpers.escapeHTML(data.showAttributes()) + "</p>");
484                output.println("The URL's for this process start with " +
485                                req.getContextPath() + req.getServletPath());
486                        }
487        /**
488         * Generate the list of tables in the database.
489         * @param data Object describing database connection
490         * @param output Object for writing web page
491         * @param req HTTP request information
492         * @param res HTTP response information
493         * @throws IOException
494         */
495        protected void processListTables(DatabaseProperties data, GenericPrinter output,
496                        HttpServletRequest req, HttpServletResponse res) throws IOException
497                        {
498                int counter = 0;
499                DatabaseMetaData meta = null;
500                ResultSet rs = null;
501                String list[] = { "TABLE" };
502
503                String rootName = req.getContextPath() + req.getServletPath();
504
505                output.println("<html><head>");
506                output.println("<title>" + rootName + "/ListTables</title>");
507                output.println("</head><body>");
508                output.println("<h1>List of Tables</h1>");
509                if (databaseDescription.length() > 0)
510                {
511                        output.println("<h2>" + databaseDescription + "</h2>");
512                }
513                try
514                {
515                        meta = data.getConnection().getMetaData();
516                        rs = meta.getTables(null, null, null, list);
517                }
518                catch (SQLException e)
519                {
520                        output.println("<p>Unable to get list of tables</p>");
521                        output.println(StringHelpers.escapeHTML(e.getClass().getName() + " " +
522                                        e.getMessage()));
523                        output.println("</body></html>");
524                        return;
525                }
526                try
527                {
528                        output.println("<table border><tr>");
529                        output.println("<td><b>Catalog</b></td><td><b>Schema</b></td><td><b>Table</b></td>");
530                        output.println("<td><b>Remarks</b></td></tr>");
531
532                        while (rs.next())
533                        {
534                                String catalog = rs.getString("TABLE_CAT");
535                                if (rs.wasNull()) {
536                                        catalog = new String();
537                                }
538                                
539                                String schema = rs.getString("TABLE_SCHEM");
540                                if (rs.wasNull()) {
541                                        schema = new String();
542                                }
543                                String item = rs.getString("TABLE_NAME");
544                                if (!isIncluded(data, catalog, schema, item))
545                                {
546                                        continue;
547                                }
548                                counter++;
549                                output.println("<tr>");
550                                output.println("<td>" + catalog + "</td><td>" + schema +
551                                                "</td><td>" + 
552                                                "<a href=\"" + rootName + "/DescribeTable?CATALOG=" + catalog +
553                                                "&SCHEMA=" + schema + "&ITEM=" + item +
554                                                "\">" + item + "</a></td><td>" +
555                                                rs.getString("REMARKS") + "</td>");
556                                output.println("</tr>");
557                        }
558                        output.println("</table>");
559                }
560                catch (SQLException e)
561                {
562                        output.println("<p>A problem was encountered while creating the list of tables</p>");
563                }
564                output.println("<p>There are " + Integer.toString(counter) + " tables</p>");
565                output.println("<hr />");
566                output.println("<p><a href=\"" + rootName + "\">Return to root page</a></p>");
567                        }
568        /**
569         * Generate the list of views in the database.
570         * @param data Object describing database connection
571         * @param output Object for writing web page
572         * @param req HTTP request information
573         * @param res HTTP response information
574         * @throws IOException
575         */
576        protected void processListViews(DatabaseProperties data, GenericPrinter output,
577                        HttpServletRequest req, HttpServletResponse res) throws IOException
578                        {
579                if (!isValid)
580                {
581
582                }
583                String list[] = { "VIEW" };
584                String catalog = null;
585                String schema = null;
586                String item = null;
587                ResultSet rs = null;
588                DatabaseMetaData meta = null;
589                int counter = 0;
590                String rootName = req.getContextPath() + req.getServletPath();
591                output.println("<html><head>");
592                output.println("<title>" + rootName + "/ListViews</title>");
593                output.println("</head><body>");
594                output.println("<h1>List of Views</h1>");
595                try
596                {
597                        meta = data.getConnection().getMetaData();
598                        rs = meta.getTables(null, null, null, list);
599                        output.println("<table border><tr><td></td><td><b>Catalog</b></td>" +
600                                        "<td><b>Schema</b></td><td><b>View Name</b></td>" +
601                                        "<td><b>Remarks</b></td></tr>");
602                        while (rs.next())
603                        {
604                                catalog = rs.getString("TABLE_CAT");
605                                schema = rs.getString("TABLE_SCHEM");
606                                item = rs.getString("TABLE_NAME");
607                                if (!isIncluded(data, catalog, schema, item))
608                                {
609                                        continue;
610                                }
611                                counter++;
612                                output.println("<tr><td>" + Integer.toString(counter) + "</td><td>" +
613                                                catalog + "</td><td>" + schema + "</td><td>" +
614                                                item + "</td><td>" + rs.getString("REMARKS") + "</td></tr>");
615                        }
616                        output.println("</table>");
617                        output.println("<p>There are " + Integer.toString(counter) + " views</p>");
618                }
619                catch (SQLException e)
620                {
621                        output.println("<p>Problem generating list of views</p>");
622                }
623
624                output.println("<p><a href=\"" + rootName + "\">Return to root page</a></p>");
625                        }
626        /**
627         * Generate the list of procedures in the database.
628         * 
629         * @param data Object containing database connection information
630         * @param output Object for writing web page
631         * @param req HTTP request information
632         * @param res HTTP response information
633         * 
634         * @throws IOException
635         * 
636         * @see DatabaseMetaData#getProcedures(String, String, String)
637         */     
638        protected void processListProcedures(DatabaseProperties data, GenericPrinter output,
639                        HttpServletRequest req, HttpServletResponse res) throws IOException
640                        {
641                ResultSet rs = null;
642                DatabaseMetaData meta = null;
643                int counter = 0;
644                String rootName = req.getContextPath() + req.getServletPath();
645                output.println("<html><head>");
646                output.println("<title>" + rootName + "/ListProcedures</title>");
647                output.println("</head><body>");
648                output.println("<h1>List of Procedures</h1>");
649                try
650                {
651                        meta = data.getConnection().getMetaData();
652                        rs = meta.getProcedures(null, null, null);
653                        counter = showResultSet(output, rs );
654                }
655                catch (SQLException e)
656                {
657                        output.println("<p>Unable to get list of procedures</p>");
658                }
659                output.println("<p>There are " + Integer.toString(counter) + " procedures</p>");
660                output.println("<p><a href=\"" + rootName + "\">Return to root page</a></p>");
661                        }
662        /**
663         * Generate the list of user defined types for the database system.
664         * 
665         * <p>Needs to have code for User Defined Types inserted.</p>
666         * 
667         * @param data Database connection information
668         * @param output GenericPrinter object to which material is sent
669         * @param req HTTP request object
670         * @param res HTTP response object
671         * @throws IOException
672         * 
673         * @see DatabaseMetaData#getUDTs(String, String, String, int[])
674         */
675        protected void processUDTInformation(DatabaseProperties data, GenericPrinter output,
676                        HttpServletRequest req, HttpServletResponse res) throws IOException
677                        {
678                String rootName = req.getContextPath() + req.getServletPath();
679                output.println("<html><head");
680                output.println("<title>User Defined Types</title>");
681                output.println("</head><body>");
682                output.println("<h1>User Defined Types</h1>");
683                output.println("<hr />");
684                output.println("<p><a href=\"" +rootName + "\">Return to root page</a></p>");
685                        }
686        /**
687         * Generate a page containing information about the database system.
688         * 
689         * @param data Object containing database connection information
690         * @param output Object for writing web page
691         * @param req HTTP request information
692         * @param res HTTP response information
693         * @throws IOException
694         * @see DatabaseMetaData
695         */     
696        protected void processDatabaseInformation(DatabaseProperties data, GenericPrinter output,
697                        HttpServletRequest req, HttpServletResponse res) throws IOException
698                        {
699                boolean hasMetaData = true;
700                DatabaseMetaData meta = null;
701                Driver driver = data.getDriver();
702                ResultSet rs = null;
703                String rootName = req.getContextPath() + req.getServletPath();
704                try
705                {
706                        meta = data.getConnection().getMetaData();
707                }
708                catch (SQLException e)
709                {
710                        hasMetaData = false;
711                        meta = null;
712                }
713                output.println("<html><head>");
714                output.println("<title>" + rootName + "/DatabaseInformation</title>");
715                output.println("</head><body>");
716                if (!hasMetaData || meta == null)
717                {
718                        output.println("<p>Unable to obtain data about database</p>");
719                        output.println("</body></html>");
720                        return;
721                }
722                else
723                {
724                        boolean transactions = true;
725                        output.println("<p><a href=\"#catalogs\">Catalogs</a> " +
726                                        "<a href=\"#schemas\">Schemas</a> " +
727                                        "<a href=\"#tables\">Tables</a> " +
728                                        "<a href=\"#procedures\">Procedures</a> " +
729                                        "</p>");
730                        try
731                        {
732                                String name = meta.getDatabaseProductName();
733                                String version = Integer.toString(meta.getDatabaseMajorVersion()) +
734                                                "." + Integer.toString(meta.getDatabaseMinorVersion());
735                                transactions = meta.supportsTransactions();
736                                output.println("<h2><a name=\"database\">Database Information</a></h2>");
737                                output.println("<p>Major version: " + Integer.toString(driver.getMajorVersion()) + "</p>");
738                                output.println("<p>Minor version: " + Integer.toString(driver.getMinorVersion()) + "</p>");
739                                output.println("<p>Using database " + name +
740                                                "  Version " + version + "</p>");
741                                output.println("<p>Supports transactions: " + Boolean.toString(transactions) + "</p>");
742                        }
743                        catch (SQLException e)
744                        {
745                                output.println("<p>Unable to get database name and version</p>");
746                        }
747                        output.println("<h2><a name=\"catalogs\">Catalog Information</a></h2>");
748                        try
749                        {
750                                boolean catalogsInDataManipulation =
751                                                meta.supportsCatalogsInDataManipulation();
752                                boolean catalogAtStart = meta.isCatalogAtStart();
753                                String catalogTerm = meta.getCatalogTerm();
754                                String catalogSeparator = meta.getCatalogSeparator();
755                                if (catalogTerm == null)
756                                {
757                                        output.println("<p>No term for catalogss</p>");
758                                }
759                                else if (catalogTerm.length() == 0)
760                                {
761                                        output.println("<p>No term for catalogs</p>");
762                                }
763                                else
764                                {
765                                        output.println("<p>Term for catalogs is " + catalogTerm + "</p>");
766                                }
767                                output.println("<p>Supports catalogs in data manipulation: " +
768                                                Boolean.toString(catalogsInDataManipulation) + "</p>");
769                                output.println("<p>Supports catalog name at start of identifier: " +
770                                                Boolean.toString(catalogAtStart));
771                                if (catalogAtStart)
772                                {
773                                        output.println("<p>Separator for catalog name is <code>" +
774                                                        catalogSeparator + "</code></p>");
775                                }
776                        }
777                        catch (SQLException e)
778                        {
779                                output.println("<p>Unable to get information about catalogs</p>");
780                        }
781                        /*
782                         * Print information on schemas.
783                         */
784                        output.println("<h2><a name=\"schemas\">Schema Information</a></h2>");
785                        try
786                        {
787                                boolean schemasInDataManipulation =
788                                                meta.supportsSchemasInDataManipulation();
789                                String schemaTerm = meta.getSchemaTerm();
790                                if (schemaTerm == null)
791                                {
792                                        output.println("<p>No term for schemas</p>");
793                                }
794                                else if (schemaTerm.length() == 0)
795                                {
796                                        output.println("<p>No term for schemas</p>");
797                                }
798                                else
799                                {
800                                        output.println("<p>Term for schemas is " + schemaTerm + "</p>");
801                                }
802                                output.println("<p>Supports schemas in data manipulation: " +
803                                                Boolean.toString(schemasInDataManipulation) + "</p>");
804                        }
805                        catch (SQLException e)
806                        {
807                                output.println("<p>Unable to get information about schemas</p>");
808                        }
809                        /*
810                         * Print information on tables
811                         */
812                        output.println("<h2><a name=\"tables\">Tables</a></h2>");
813                        try
814                        {
815                                int counter = 0;
816                                rs = meta.getTableTypes();
817                                output.println("<p>The table types are </p>");
818                                counter = showResultSet(output, rs);
819                                output.println("<p>There are " + Integer.toString(counter) + " table types</p>");
820                        }
821                        catch(SQLException e)
822                        {
823                                output.println("<p>Unable to get information on table types</p>");
824                        }
825                        /*
826                         * Print information on procedures.
827                         */
828                        output.println("<h2><a name=\"procedures\">Procedures</a></h2>");
829                        try
830                        {
831                                String value = meta.getProcedureTerm();
832                                output.println("<p>Term for procedure is " + value + "</p>");
833                        }
834                        catch (SQLException e)
835                        {
836                                output.println("<p>Unable to get information on procedures</p>");
837                        }
838                }
839                output.println("<hr />");
840                output.println("<p><a href=\"" + rootName + "\">Return to root page</p>");
841                        }
842        /**
843         * Generate a web page describing the layout of a table.
844         * 
845         * @param data Database connection data object
846         * @param output GenericPrinter object to which information is sent
847         * @param req HTTP request object
848         * @param res HTTP response object
849         * @throws IOException
850         * @see DatabaseMetaData#getTables(String, String, String, String[])
851         * @see DatabaseMetaData#getColumns(String, String, String, String)
852         * @see DatabaseMetaData#getIndexInfo(String, String, String, boolean, boolean)
853         * @see DatabaseMetaData#getExportedKeys(String, String, String)
854         * @see DatabaseMetaData#getImportedKeys(String, String, String)
855         */
856        protected void processDescribeTable(DatabaseProperties data, GenericPrinter output, 
857                        HttpServletRequest req, HttpServletResponse res) throws IOException
858                        {
859                String catalog = null;
860                String schema = null;
861                String item = null;
862                int counter = -1;
863                String rootName = req.getContextPath() + req.getServletPath();
864                DatabaseMetaData meta = null;
865                ResultSet rs = null;
866                try
867                {
868                        catalog = req.getParameter("CATALOG");
869                        if (catalog.length()==0) {
870                                catalog=null;
871                        }
872
873                        schema = req.getParameter("SCHEMA");
874                                                if (schema.length()==0) {
875                                schema = null;
876                        }
877                        item = req.getParameter("ITEM");
878                        output.println("<html><head>");
879                        output.println("<title>Layout of Table " + item + "</title>");
880                        output.println("</head><body>");
881                        output.println("<h1>Layout of Table " + item + "</h1>");
882                        if (catalog != null) {
883                                output.println("<p>Catalog: " + catalog + "</p>");
884                        }
885                        if (schema != null) {
886                                output.println("<p>Schema: " + schema + "</p>");
887                        }
888                        output.println("<p>Running processDescribeTable</p>");
889                        meta = data.getConnection().getMetaData();
890                        output.println("<p>Result of getColumns</p>");
891                        rs = meta.getColumns(catalog, schema, item, (String) null);
892                        output.println("<p>Information on columns</p>");
893                        counter = showResultSet(output, rs);
894                        output.println("<p>There are " + Integer.toString(counter) + " columns</p>");
895                        rs = null;
896                        output.println("<hr />");
897                        output.println("<p>Result of getIndexInfo</p>");
898                        rs = meta.getIndexInfo(catalog, schema, item, false, false);
899                        counter = showResultSet(output, rs);
900                        output.println("<p>There are " + Integer.toString(counter) + " indexes</p>");
901                        rs = null;
902                        output.println("<hr />");
903                        output.println("<p>Result of getImportedKeys</p>");
904                        rs = meta.getImportedKeys(catalog, schema, item);
905                        counter = showResultSet(output, rs);
906                        output.println("<p>There are " + Integer.toString(counter) + " imported keys</p>");
907                        rs = null;
908                        output.println("<hr />");
909                        output.println("<p>Result of getExportedKeys</p>");
910                        rs = meta.getExportedKeys(catalog, schema, item);
911                        counter = showResultSet(output, rs);
912                        output.println("<p>There are " + Integer.toString(counter) + " exported keys</p>");
913                        rs = null;
914                        output.println("<hr />");
915                        output.println("<p><a href=\"" + rootName + "\">Return to root page</a></p>");  
916                }
917                catch (SQLException e)
918                {
919                        System.out.println("<p>Unable to get information</p>");
920                        System.out.println("<hr />");
921                        System.out.println("<p><a href=\"" + rootName + "\">Return to root page</a></p>");
922                }
923                        }
924
925        /**
926         * Displays a result set as an HTML table.
927         * @param output Object for printing to HTML page
928         * @param rs Result set to be processed
929         * @return number of rows
930         */
931        protected int showResultSet(GenericPrinter output, ResultSet rs)
932        {
933                int counter = 0;
934                ResultSetMetaData rsmeta = null;
935                try
936                {
937                        rsmeta = rs.getMetaData();
938                }
939                catch (SQLException e)
940                {
941                        output.println("<p>Unable to get metadata</p>");
942                        output.println(StringHelpers.escapeHTML(e.getClass().getName() + " " +
943                                        e.getMessage()));
944                        output.println("</body></html>");
945                        return -1;
946                }
947                try
948                {
949                        int columnCount = rsmeta.getColumnCount();
950                        int columnType[] = new int[columnCount];
951                        output.println("<table border><tr><td>&nbsp;</td>");
952                        for (int i = 1; i <= columnCount; i++)
953                        {
954                                columnType[i-1] = rsmeta.getColumnType(i);
955                                output.println("<td><b><code>" + 
956                                                StringHelpers.escapeHTML(rsmeta.getColumnName(i)) + "</code></b></td>");
957                        }
958                        output.println("</tr>");
959                        while (rs.next())
960                        {
961                                counter++;
962                                output.println("<tr><td align=\"right\">" + Integer.toString(counter) +
963                                                "</td>");
964                                for (int i = 1; i <= columnCount; i++)
965                                {
966                                        int localType = columnType[i-1];
967                                        if (localType == Types.BOOLEAN)
968                                        {
969                                                output.println("<td>" + Boolean.toString(rs.getBoolean(i)) + "</td>");
970                                        }
971                                        else if (localType == Types.VARCHAR || localType == Types.CHAR)
972                                        {
973                                                output.println("<td><code>" + StringHelpers.escapeHTML(rs.getString(i)) + "</code></td>");
974                                        }
975                                        else
976                                        {
977                                                output.println("<td align=\"right\"><code>" + 
978                                                                StringHelpers.escapeHTML(rs.getString(i)) + "</code></td>");
979                                        }
980                                }
981                                output.println("</tr>");
982                        }
983                        output.println("</table>");
984                }
985                catch (SQLException e)
986                {
987                        output.println("<p>Problems generating list</p>");
988                        return -1;
989                }
990                return counter;
991        }
992}