001: /*
002:
003: Derby - Class org.apache.derbyBuild.ErrorMessageGenerator
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to You under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derbyBuild;
023:
024: import java.io.PrintWriter;
025: import java.io.FileOutputStream;
026: import java.util.Hashtable;
027: import java.lang.Math;
028: import java.sql.Connection;
029: import java.sql.DriverManager;
030: import java.sql.Statement;
031: import java.sql.ResultSet;
032:
033: /**
034: *
035: * This tool is used to generate the DITA file that lists all the SQL states
036: * and their error messages.
037: */
038: public class ErrorMessageGenerator {
039: /** Driver name */
040: private static final String DERBY_EMBEDDED_DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
041:
042: /** Output file name */
043: private static final String DITA_FILE_NAME = "rrefexcept71493.dita";
044:
045: /** Location of output file in documentation client. The root of the
046: documentation client is the directory just above src.
047: */
048: private static final String OUTPUT_FILE_STUB = "src/ref/"
049: + DITA_FILE_NAME;
050:
051: /** Usage string */
052: private static final String USAGE_STRING = "Usage:\n"
053: + "\n"
054: + " java org.apache.derbyBuild.ErrorMessageGenerator DOC_ROOT\n"
055: + "\n"
056: + " where DOC_ROOT = Root of documentation client (the directory just\n"
057: + " above src). E.g., /home/myname/derby/docs/trunk\n";
058:
059: /** Success exit */
060: private static final int SUCCESS = 0;
061: /** Failure exit */
062: private static final int FAILURE = 1;
063:
064: /** Root of the documentation client */
065: private String docClientRoot;
066:
067: /** The name of the DITA file */
068: private String ditaFileName = DITA_FILE_NAME;
069:
070: /** The connection URL */
071: private String url = "jdbc:derby:wombat;create=true";
072:
073: /** Used to write to the DITA file */
074: private PrintWriter ditaWriter;
075:
076: /** Table of SQL State codes and their meaning */
077: private static Hashtable codes = new Hashtable();
078:
079: /** short-hand for a double-quote */
080: char dq = '"';
081:
082: /** Indicates whether we are on the first table */
083: boolean firstTable = true;
084:
085: /** Stores the code for the current row */
086: String currentCode;
087:
088: /** Stores the class comment for the current row */
089: String currentComment;
090:
091: static {
092: codes.put("0A", "Feature not supported");
093: codes.put("01", "Warning");
094: codes.put("04", "Database authentication");
095: codes.put("07", "Dynamic SQL Error");
096: codes.put("08", "Connection Exception");
097: codes.put("21", "Cardinality Violation");
098: codes.put("22", "Data Exception");
099: codes.put("23", "Constraint Violation ");
100: codes.put("24", "Invalid Cursor State");
101: codes.put("25", "Invalid Transaction State");
102: codes.put("28", "Invalid Authorization Specification");
103: codes.put("2D", "Invalid Transaction Termination");
104: codes.put("38", "External Function Exception");
105: codes.put("39", "External Routine Invocation Exception");
106: codes.put("3B", "Invalid SAVEPOINT");
107: codes.put("40", "Transaction Rollback");
108: codes.put("42", "Syntax Error or Access Rule Violation");
109: codes.put("57", "DRDA Network Protocol: Execution Failure");
110: codes.put("58", "DRDA Network Protocol: Protocol Error");
111: codes.put("X0", "Execution exceptions");
112: codes.put("XBCA", "CacheService");
113: codes.put("XBCM", "ClassManager");
114: codes.put("XBCX", "Cryptography");
115: codes.put("XBM", "Monitor");
116: codes.put("XCL", "Execution exceptions");
117: codes.put("XCW", "Upgrade unsupported");
118: codes.put("XCX", "Internal Utility Errors");
119: codes.put("XCY", "Derby Property Exceptions");
120: codes.put("XCZ", "org.apache.derby.database.UserUtility");
121: codes.put("XD00", "Dependency Manager");
122: codes.put("XIE", "Import/Export Exceptions");
123: codes.put("XJ", "Connectivity Errors");
124: codes.put("XN", "Network Client Exceptions");
125: codes.put("XSAI", "Store - access.protocol.interface");
126: codes.put("XSAM", "Store - AccessManager");
127: codes.put("XSAS", "Store - Sort");
128: codes.put("XSAX", "Store - access.protocol.XA statement");
129: codes.put("XSCB", "Store - BTree");
130: codes.put("XSCG0", "Conglomerate");
131: codes.put("XSCH", "Heap");
132: codes.put("XSDA", "RawStore - Data.Generic statement");
133: codes.put("XSDB", "RawStore - Data.Generic transaction");
134: codes.put("XSDF", "RawStore - Data.Filesystem statement");
135: codes.put("XSDG", "RawStore - Data.Filesystem database");
136: codes.put("XSLA", "RawStore - Log.Generic database exceptions");
137: codes
138: .put("XSLB",
139: "RawStore - Log.Generic statement exceptions");
140: codes.put("XSRS", "RawStore - protocol.Interface statement");
141: codes.put("XSTA2", "XACT_TRANSACTION_ACTIVE");
142: codes.put("XSTB", "RawStore - Transactions.Basic system");
143: codes.put("XXXXX", "No SQLSTATE");
144: }
145:
146: /**
147: * <p>
148: * Generate the dita file of SQLStates for inclusion in
149: * Derby's Reference Guide.
150: * </p>
151: *
152: * <ul>
153: * <li>args[ 0 ] = Root of docs client. E.g. "/home/myname/derby/docs/trunk"</li>
154: * </ul>
155: */
156: public static void main(String[] args) {
157: ErrorMessageGenerator generator = new ErrorMessageGenerator();
158:
159: if (!generator.parseArgs(args)) {
160: generator.printUsage();
161:
162: System.exit(FAILURE);
163: }
164:
165: generator.setDitaFileName(generator.docClientRoot + '/'
166: + OUTPUT_FILE_STUB);
167:
168: try {
169: generator.execute();
170: } catch (Throwable t) {
171: t.printStackTrace();
172: System.exit(FAILURE);
173: }
174: System.exit(SUCCESS);
175: }
176:
177: /**
178: * <p>
179: * Parse the arguments. Returns false if the arguments are malformed.
180: * </p>
181: *
182: * <ul>
183: * <li>args[ 0 ] = Root of docs client. E.g. "/home/myname/derby/docs"</li>
184: * </ul>
185: */
186: private boolean parseArgs(String args[]) {
187: int idx = 0;
188:
189: if (args == null) {
190: return false;
191: }
192: if (args.length != 1) {
193: return false;
194: }
195:
196: docClientRoot = args[idx++];
197:
198: return true;
199: }
200:
201: /**
202: * <p>
203: * Print instructions on how to run this program.
204: * </p>
205: */
206: private void printUsage() {
207: System.out.println(USAGE_STRING);
208: }
209:
210: /**
211: * Set the name of the DITA file
212: */
213: public void setDitaFileName(String ditaFileName) {
214: this .ditaFileName = ditaFileName;
215: }
216:
217: /**
218: * Set the database URL
219: */
220: public void setDatabaseUrl(String url) {
221: this .url = url;
222: }
223:
224: /**
225: * Execute the program
226: */
227: public void execute() throws Exception {
228: try {
229: // Open the DITA file
230: ditaWriter = openDitaFile();
231:
232: // Generate the header of the DITA file
233: generateDitaHeader();
234:
235: // Generate the error messages for the DITA file
236: generateMessages();
237:
238: // Generate the footer of the DITA file
239: generateDitaFooter();
240:
241: ditaWriter.close();
242: } catch (Exception e) {
243: throw e;
244: } finally {
245: if (ditaWriter != null) {
246: ditaWriter.close();
247: }
248: }
249: }
250:
251: /**
252: * Open the DITA file for writing
253: *
254: * @return a PrintWriter for the DITA file
255: */
256: protected PrintWriter openDitaFile() throws Exception {
257: return new PrintWriter(new FileOutputStream(ditaFileName));
258: }
259:
260: /**
261: * Generate the header for the DITA file
262: */
263: protected void generateDitaHeader() throws Exception {
264: PrintWriter dw = this .ditaWriter;
265:
266: ditaWriter.println("<?xml version=" + dq + "1.0" + dq
267: + " encoding=" + dq + "utf-8" + dq + "?>");
268: ditaWriter.println("<!DOCTYPE reference PUBLIC " + dq
269: + "-//OASIS//DTD DITA Reference//EN" + dq);
270: ditaWriter.println(dq + "../dtd/reference.dtd" + dq + ">");
271: ditaWriter.println("<reference id=" + dq + "rrefexcept71493"
272: + dq + " xml:lang=" + dq + "en-us" + dq + ">");
273: ditaWriter.println("<!-- ");
274: ditaWriter
275: .println("Licensed to the Apache Software Foundation (ASF) under one or more");
276: ditaWriter
277: .println("contributor license agreements. See the NOTICE file distributed with");
278: ditaWriter
279: .println("this work for additional information regarding copyright ownership.");
280: ditaWriter
281: .println("The ASF licenses this file to You under the Apache License, Version 2.0");
282: ditaWriter
283: .println("(the \"License\"); you may not use this file except in compliance with");
284: ditaWriter
285: .println("the License. You may obtain a copy of the License at ");
286: ditaWriter.println("");
287: ditaWriter
288: .println("http://www.apache.org/licenses/LICENSE-2.0 ");
289: ditaWriter.println("");
290: ditaWriter
291: .println("Unless required by applicable law or agreed to in writing, software ");
292: ditaWriter
293: .println("distributed under the License is distributed on an \"AS IS\" BASIS, ");
294: ditaWriter
295: .println("WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ");
296: ditaWriter
297: .println("See the License for the specific language governing permissions and ");
298: ditaWriter.println("limitations under the License.");
299: ditaWriter.println("-->");
300: ditaWriter.println("<!-- ");
301: ditaWriter
302: .println("NOTE: this file is generated by running org.apache.derbyBuild.ErrorMessageGenerator ");
303: ditaWriter
304: .println("This utility reads all the error messages from the database and ");
305: ditaWriter
306: .println("generates this file. Please do not feel obligated to update it manually ");
307: ditaWriter.println("-->");
308: ditaWriter
309: .println("<title>SQL error messages and exceptions</title>");
310: ditaWriter.println("<refbody>");
311: ditaWriter.println("<section><p>The following tables list "
312: + "<i>SQLStates</i> for exceptions. Exceptions ");
313: ditaWriter
314: .println("that begin with an <i>X</i> are specific to "
315: + "<ph conref=" + dq
316: + "refconrefs.dita#prod/productshortname" + dq
317: + "></ph>.");
318: ditaWriter
319: .println("In the messages below each {n} tag, where n is a number, represents a\n"
320: + "value that the Derby engine fills in at runtime. Examples of values\n"
321: + "include database names, database object names, property names, user\n"
322: + "names, and parameters passed to a function or procedure:\n");
323: ditaWriter.println("</p></section>");
324: ditaWriter.println("<section>");
325: }
326:
327: /**
328: * Generate the footer for the DITA file
329: */
330: protected void generateDitaFooter() throws Exception {
331: ditaWriter.println("</section>");
332: ditaWriter.println("</refbody>");
333: ditaWriter.println("</reference>");
334: }
335:
336: /**
337: * Generate the actual error messages
338: */
339: protected void generateMessages() throws Exception {
340: // Get the list of messages from the database
341: ResultSet rs = getMessages();
342:
343: String prevSqlState = null;
344: while (rs.next()) {
345: String sqlState = rs.getString(1);
346: String message = replaceSpecialChars(rs.getString(2));
347: String severity = rs.getString(3);
348:
349: // See if it's a new SQL State category, and if so,
350: // start a new table in the DITA file
351: testForNewCategory(sqlState, prevSqlState);
352:
353: generateTableEntry(sqlState, message, severity);
354: prevSqlState = sqlState;
355: }
356:
357: // Tidy up the last table.
358: generateTableFooter();
359: }
360:
361: /**
362: * Replace a substring with some equivalent. For example, we would
363: * like to replace "<" with "<" in the error messages.
364: * Add any substrings you would like to replace in the code below.
365: * Be aware that the first paramter to the replaceAll() method is
366: * interpreted as a regular expression.
367: *
368: * @param input
369: * A String that may contain substrings that we want to replace
370: * @return
371: * Output String where substrings selected for replacement have been
372: * replaced.
373: * @see java.util.regex.Pattern
374: */
375: private static String replaceSpecialChars(java.lang.String input) {
376: String output = input.replaceAll("<", "<");
377: output = output.replaceAll(">", ">");
378:
379: return output;
380: }
381:
382: /**
383: * Test to see if we have a new SQL State category, and if so,
384: * end the old table and start a new table in the DITA file.
385: *
386: * @param sqlState
387: * The SQL State for the current row
388: *
389: * @param oldSqlState
390: * The SQL State for the previous row
391: */
392: protected void testForNewCategory(String sqlState,
393: String prevSqlState) throws Exception {
394: String prevCode = currentCode;
395: currentCode = getCode(sqlState);
396:
397: if (currentCode == null) {
398: ditaWriter
399: .println("Unable to determine code for SQL State "
400: + sqlState);
401: System.out
402: .println("Unable to determine code for SQL State "
403: + sqlState);
404: return;
405: }
406:
407: if (currentCode.equals(prevCode)) {
408: return;
409: }
410:
411: // If we got here, it's a new prefix, let's end the old table
412: // and generate a header for a new table
413: generateTableHeader();
414: }
415:
416: /**
417: * Get the class for the current SQL State.
418: * SIDE EFFECT: sets this.currentComment
419: */
420: protected String getCode(String sqlState) throws Exception {
421: String comment = null;
422: String code = null;
423:
424: if (sqlState == null) {
425: return null;
426: }
427:
428: int codeLen = sqlState.length();
429:
430: while (codeLen >= 2) {
431: code = sqlState.substring(0, codeLen);
432: comment = (String) codes.get(code);
433: if (comment != null) {
434: this .currentComment = comment;
435: return code;
436: }
437: codeLen--;
438: }
439:
440: if (comment == null) {
441: return null;
442: }
443:
444: return code;
445: }
446:
447: /**
448: * Generate the table header for a given prefix
449: */
450: protected void generateTableHeader() throws Exception {
451: // Generate the end of the previous table
452: if (!firstTable) {
453: generateTableFooter();
454: } else {
455: firstTable = false;
456: }
457:
458: // Generate the header for this table
459: ditaWriter.println("<table><title>Class " + currentCode + ": "
460: + currentComment + "</title>");
461: ditaWriter.println("<tgroup cols=" + dq + "2" + dq
462: + "><colspec colname=" + dq + "col1" + dq
463: + " colwidth=" + dq + "1*" + dq + "/><colspec colname="
464: + dq + "col2" + dq);
465: ditaWriter.println("colwidth=" + dq + "7.5*" + dq + "/>");
466: ditaWriter.println("<thead>");
467: ditaWriter.println("<row valign=" + dq + "bottom" + dq + ">");
468: ditaWriter.println("<entry colname=" + dq + "col1" + dq
469: + ">SQLSTATE</entry>");
470: ditaWriter.println("<entry colname=" + dq + "col2" + dq
471: + ">Message Text</entry>");
472: ditaWriter.println("</row>");
473: ditaWriter.println("</thead>");
474: ditaWriter.println("<tbody>");
475: }
476:
477: /**
478: * Generate the table footer for a given prefix
479: */
480: protected void generateTableFooter() throws Exception {
481: ditaWriter.println("</tbody>");
482: ditaWriter.println("</tgroup>");
483: ditaWriter.println("</table>");
484: }
485:
486: /**
487: * Generate a table entry for the current row
488: */
489: protected void generateTableEntry(String sqlState, String message,
490: String severity) throws Exception {
491: ditaWriter.println("<row>");
492: ditaWriter.println("<entry colname =" + dq + "col1" + dq + ">"
493: + sqlState + "</entry>");
494: ditaWriter.println("<entry colname =" + dq + "col2" + dq + ">"
495: + message + "</entry>");
496: ditaWriter.println("</row>");
497: }
498:
499: /**
500: * Get the messages from the database
501: */
502: protected ResultSet getMessages() throws Exception {
503: Class.forName(DERBY_EMBEDDED_DRIVER);
504:
505: Connection conn = DriverManager.getConnection(url);
506:
507: if (conn == null) {
508: throw new Exception("Unable to connect to " + url);
509: }
510:
511: Statement stmt = conn.createStatement();
512:
513: ResultSet rs = stmt
514: .executeQuery("SELECT SQL_STATE, MESSAGE, SEVERITY FROM "
515: + "new org.apache.derby.diag.ErrorMessages() AS vti "
516: + "ORDER BY SQL_STATE");
517:
518: return rs;
519: }
520: }
|