001: package org.apache.ojb.broker.ant;
002:
003: /* Copyright 2002-2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: import java.lang.reflect.Constructor;
019: import java.lang.reflect.InvocationTargetException;
020: import java.net.MalformedURLException;
021: import java.sql.Driver;
022: import java.sql.DriverManager;
023: import java.sql.SQLException;
024: import java.sql.SQLWarning;
025: import java.util.ArrayList;
026: import java.util.Collection;
027:
028: import org.apache.ojb.broker.PersistenceBrokerException;
029: import org.apache.ojb.broker.locking.IsolationLevels;
030: import org.apache.ojb.broker.metadata.*;
031: import org.apache.ojb.broker.util.logging.Logger;
032: import org.apache.ojb.broker.util.logging.LoggerFactory;
033: import org.xml.sax.Attributes;
034: import org.xml.sax.SAXException;
035: import org.xml.sax.SAXParseException;
036: import org.xml.sax.helpers.DefaultHandler;
037:
038: /**
039: * A SAX parser handler used by the Ant VerifyMappingsTask to process an
040: * OJB DescriptorRepository xml file.
041: *
042: * @author <a href="mailto:daren@softwarearena.com">Daren Drummond</a>
043: * @version $Id: RepositoryVerifierHandler.java,v 1.16.2.2 2005/12/21 22:24:16 tomdz Exp $
044: */
045: public class RepositoryVerifierHandler extends DefaultHandler implements
046: RepositoryElements, IsolationLevels {
047: private Logger logger;
048:
049: private DescriptorRepository m_repository;
050: private JdbcConnectionDescriptor m_CurrentJCD;
051: private ClassDescriptor m_CurrentCLD;
052: private FieldDescriptor m_CurrentFLD;
053: private ObjectReferenceDescriptor m_CurrentORD;
054: private CollectionDescriptor m_CurrentCOD;
055: private String m_CurrentString;
056: private VerifyMappingsTask m_callingTask;
057: private DBUtility m_dBUtility;
058:
059: //ClassDescriptor members;
060: //private String m_CurrentClass = "";
061: private Class m_currentClass = null;
062: private String m_CurrentTable = null;
063: private boolean m_currentTableExists = false;
064:
065: /** the default isolation level*/
066: private int defIsoLevel = IL_DEFAULT;
067:
068: /**
069: * All known xml tags are kept in this table.
070: * The tags table allows lookup from literal to id
071: * and from id to literal.
072: */
073: private RepositoryTags tags = RepositoryTags.getInstance();
074:
075: private Collection m_VerifyExceptions = new ArrayList(69);
076: private Collection m_VerifyWarnings = new ArrayList(69);
077:
078: /**
079: * Allows not to specify field id.
080: */
081: private int m_lastId = 0;
082:
083: /**
084: * The only public constructor for RepositoryVerifierHandler.
085: *
086: * @param callingTask A reference to the ant task using this parser.
087: */
088: public RepositoryVerifierHandler(VerifyMappingsTask callingTask) {
089: m_callingTask = callingTask;
090: m_callingTask.logWarning("Loaded RepositoryVerifierHandler.");
091: logger = LoggerFactory.getLogger(this .getClass());
092: }
093:
094: /**
095: * returns the XmlCapable id associated with the literal.
096: * OJB maintains a RepositoryTags table that provides
097: * a mapping from xml-tags to XmlCapable ids.
098: *
099: * @param literal the literal to lookup
100: * @return the int value representing the XmlCapable
101: *
102: * @throws MetadataException if no literal was found in tags mapping
103: */
104: private int getLiteralId(String literal)
105: throws PersistenceBrokerException {
106: ////logger.debug("lookup: " + literal);
107: try {
108: return tags.getIdByTag(literal);
109: } catch (NullPointerException t) {
110: throw new MetadataException("unknown literal: '" + literal
111: + "'", t);
112: }
113:
114: }
115:
116: /**
117: * startDocument callback, nothing to do here.
118: */
119: public void startDocument() {
120: //logger.debug("startDoc");
121: }
122:
123: /**
124: * endDocument callback, nothing to do here.
125: */
126: public void endDocument() {
127: //logger.debug("endDoc");
128: }
129:
130: public void startElement(String uri, String name, String qName,
131: Attributes atts) {
132: m_CurrentString = null;
133: try {
134:
135: switch (getLiteralId(qName)) {
136: case MAPPING_REPOSITORY: {
137: String defIso = atts.getValue(tags
138: .getTagById(ISOLATION_LEVEL));
139: if (defIso != null) {
140: defIsoLevel = getIsoLevel(defIso);
141: }
142:
143: break;
144: }
145: case JDBC_CONNECTION_DESCRIPTOR: {
146: m_CurrentJCD = new JdbcConnectionDescriptor();
147:
148: // set platform attribute
149: String platform = atts.getValue(tags
150: .getTagById(DBMS_NAME));
151: //logger.debug(" " + tags.getTagById(Dbms_name) + ": " + platform);
152: m_CurrentJCD.setDbms(platform);
153:
154: // set jdbc-level attribute
155: String level = atts.getValue(tags
156: .getTagById(JDBC_LEVEL));
157: //logger.debug(" " + tags.getTagById(Jdbc_level) + ": " + level);
158: m_CurrentJCD.setJdbcLevel(level);
159:
160: // set driver attribute
161: String driver = atts.getValue(tags
162: .getTagById(DRIVER_NAME));
163: //logger.debug(" " + tags.getTagById(Driver_name) + ": " + driver);
164: m_CurrentJCD.setDriver(driver);
165:
166: // set protocol attribute
167: String protocol = atts.getValue(tags
168: .getTagById(URL_PROTOCOL));
169: //logger.debug(" " + tags.getTagById(Url_protocol) + ": " + protocol);
170: m_CurrentJCD.setProtocol(protocol);
171:
172: // set subprotocol attribute
173: String subprotocol = atts.getValue(tags
174: .getTagById(URL_SUBPROTOCOL));
175: //logger.debug(" " + tags.getTagById(Url_subprotocol) + ": " + subprotocol);
176: m_CurrentJCD.setSubProtocol(subprotocol);
177:
178: // set the dbalias attribute
179: String dbalias = atts.getValue(tags
180: .getTagById(URL_DBALIAS));
181: //logger.debug(" " + tags.getTagById(Url_dbalias) + ": " + dbalias);
182: m_CurrentJCD.setDbAlias(dbalias);
183:
184: // set the datasource attribute
185: String datasource = atts.getValue(tags
186: .getTagById(DATASOURCE_NAME));
187: //logger.debug(" " + tags.getTagById(Datasource_name) + ": " + datasource);
188: m_CurrentJCD.setDatasourceName(datasource);
189:
190: // set the user attribute
191: String user = atts.getValue(tags.getTagById(USER_NAME));
192: //logger.debug(" " + tags.getTagById(User_name) + ": " + user);
193: m_CurrentJCD.setUserName(user);
194:
195: // set the password attribute
196: String password = atts.getValue(tags
197: .getTagById(USER_PASSWD));
198: //logger.debug(" " + tags.getTagById(User_passwd) + ": " + password);
199: m_CurrentJCD.setPassWord(password);
200:
201: //connect to the database
202: m_dBUtility = getDBUtility(m_CurrentJCD);
203:
204: break;
205: }
206: case CLASS_DESCRIPTOR: {
207: String isoLevel = atts.getValue(tags
208: .getTagById(ISOLATION_LEVEL));
209:
210: // set class attribute
211: String classname = atts.getValue(tags
212: .getTagById(CLASS_NAME));
213:
214: try {
215: m_currentClass = m_callingTask.loadClass(classname);
216: } catch (ClassNotFoundException ex) {
217: //logger.error(ex);
218: throw new MetadataException(
219: "Can't load class-descriptor class '"
220: + classname + "'.");
221: }
222:
223: m_CurrentTable = atts.getValue(tags
224: .getTagById(TABLE_NAME));
225:
226: if (m_CurrentTable != null) {
227: m_currentTableExists = m_dBUtility
228: .exists(m_CurrentTable);
229: if (!m_currentTableExists) {
230: throw new MetadataException("The table '"
231: + m_CurrentTable
232: + "' does not exist in the database.");
233: }
234: }
235:
236: break;
237: }
238:
239: case CLASS_EXTENT: {
240:
241: String classname = atts.getValue("class-ref");
242:
243: try {
244: Class classExtent = m_callingTask
245: .loadClass(classname);
246: } catch (ClassNotFoundException ex) {
247: //logger.error(ex);
248: throw new MetadataException(
249: "Can't load extent-class class '"
250: + classname + "'.");
251: }
252: break;
253: }
254:
255: case FIELD_DESCRIPTOR: {
256: String strId = atts.getValue("id");
257: m_lastId = (strId == null ? m_lastId + 1 : Integer
258: .parseInt(strId));
259: m_CurrentFLD = new FieldDescriptor(null, m_lastId);
260:
261: String fieldName = atts.getValue(tags
262: .getTagById(FIELD_NAME));
263:
264: if (m_currentClass != null) {
265: //this.m_callingTask.logWarning("Verifying " + fieldName + " in class " + m_currentClass.toString());
266: confirmFieldExists(m_currentClass, fieldName);
267:
268: String columnName = atts.getValue(tags
269: .getTagById(COLUMN_NAME));
270: m_CurrentFLD.setColumnName(columnName);
271:
272: String jdbcType = atts.getValue(tags
273: .getTagById(JDBC_TYPE));
274: m_CurrentFLD.setColumnType(jdbcType);
275:
276: //check that the field exists in the database
277: if (m_currentTableExists) {
278: if (this .m_callingTask
279: .getUseStrictTypeChecking()) {
280: m_dBUtility.exists(m_CurrentTable,
281: columnName, jdbcType, m_callingTask
282: .getIgnoreFieldNameCase());
283: } else {
284: m_dBUtility.existsUseWarnings(
285: m_CurrentTable, columnName,
286: jdbcType, m_callingTask
287: .getIgnoreFieldNameCase());
288: }
289: }
290:
291: }
292:
293: break;
294: }
295: case REFERENCE_DESCRIPTOR: {
296: if (m_currentClass != null) {
297: //Confirm that this field exists in the class
298: name = atts.getValue(tags.getTagById(FIELD_NAME));
299: confirmFieldExists(m_currentClass, name);
300:
301: String classRef = atts.getValue(tags
302: .getTagById(REFERENCED_CLASS));
303: try {
304: //Confirm that class 'class-ref' can be loaded.
305: Class refClass = m_callingTask
306: .loadClass(classRef);
307: } catch (ClassNotFoundException ex) {
308: //logger.error(ex);
309: throw new MetadataException(
310: "Can't find class-ref '" + classRef
311: + "' in reference-descriptor '"
312: + name + "'.");
313: }
314:
315: }
316:
317: break;
318: }
319:
320: case COLLECTION_DESCRIPTOR: {
321:
322: if (m_currentClass != null) {
323: //Confirm that this field exists in the class
324: name = atts.getValue(tags.getTagById(FIELD_NAME));
325: confirmFieldExists(m_currentClass, name);
326:
327: String collectionClass = atts.getValue(tags
328: .getTagById(COLLECTION_CLASS));
329: //logger.debug(" " + tags.getTagById(Collection_class) + ": " + collectionClass);
330: if (collectionClass != null) {
331: try {
332: //Confirm that class 'class-ref' can be loaded.
333: Class oCollectionClass = m_callingTask
334: .loadClass(collectionClass);
335: } catch (ClassNotFoundException ex) {
336: //logger.error(ex);
337: throw new MetadataException(
338: "Can't find collection-class '"
339: + collectionClass
340: + "' in collection-descriptor '"
341: + name + "'.");
342: }
343: }
344: // set element-class-ref attribute
345: String elementClassRef = atts.getValue(tags
346: .getTagById(ITEMS_CLASS));
347: //logger.debug(" " + tags.getTagById(Items_class) + ": " + elementClassRef);
348: if (elementClassRef != null) {
349: try {
350: //Confirm that class 'class-ref' can be loaded.
351: Class oElementClassRef = m_callingTask
352: .loadClass(elementClassRef);
353: } catch (ClassNotFoundException ex) {
354: //logger.error(ex);
355: throw new MetadataException(
356: "Can't find element-class-ref '"
357: + elementClassRef
358: + "' in collection-descriptor '"
359: + name + "'.");
360: }
361: }
362: }
363:
364: break;
365: }
366:
367: default: {
368: // nop
369: }
370: }
371: } catch (MetadataException mde) {
372: this .m_callingTask.logWarning(" --> Mapping Error: "
373: + mde.getMessage());
374: m_VerifyExceptions.add(mde);
375: } catch (NullPointerException garbage) {
376: //eat it.
377: } catch (SQLWarning sqlw) {
378: this .m_callingTask.logInfo(" --> DB Mapping Warning: "
379: + sqlw.getMessage());
380: m_VerifyWarnings.add(sqlw);
381: } catch (SQLException sqle) {
382: this .m_callingTask.logWarning(" --> DB Mapping Error: "
383: + sqle.getMessage());
384: m_VerifyExceptions.add(sqle);
385: } catch (Exception ex) {
386: logger.error(ex);
387: throw new PersistenceBrokerException(ex);
388: }
389: }
390:
391: public void endElement(String uri, String name, String qName) {
392: try {
393: switch (getLiteralId(qName)) {
394: case MAPPING_REPOSITORY: {
395: //release the any db connections
396: if (m_dBUtility != null)
397: m_dBUtility.release();
398: break;
399: }
400: case JDBC_CONNECTION_DESCRIPTOR: {
401: //logger.debug(" < " + tags.getTagById(JdbcConnectionDescriptor));
402: break;
403: }
404: case CLASS_DESCRIPTOR: {
405: //logger.debug(" < " + tags.getTagById(ClassDescriptor));
406: m_currentClass = null;
407: m_CurrentTable = null;
408: m_currentTableExists = false;
409: m_CurrentCLD = null;
410: break;
411: }
412: case CLASS_EXTENT: {
413: break;
414: }
415:
416: case FIELD_DESCRIPTOR: {
417: //logger.debug(" < " + tags.getTagById(FIELDDESCRIPTOR));
418: m_CurrentFLD = null;
419: break;
420: }
421: case REFERENCE_DESCRIPTOR: {
422: //logger.debug(" < " + tags.getTagById(ReferenceDescriptor));
423: m_CurrentORD = null;
424: break;
425: }
426: case FOREIGN_KEY: {
427: //logger.debug(" < " + tags.getTagById(FOREIGN_KEY));
428: }
429:
430: case COLLECTION_DESCRIPTOR: {
431: //logger.debug(" < " + tags.getTagById(CollectionDescriptor));
432: m_CurrentCOD = null;
433: break;
434: }
435:
436: case INVERSE_FK: {
437: //logger.debug(" < " + tags.getTagById(Inverse_fk));
438: break;
439: }
440:
441: case FK_POINTING_TO_THIS_CLASS: {
442: //logger.debug(" < " + tags.getTagById(Fk_pointing_to_this_class));
443: break;
444: }
445: case FK_POINTING_TO_ITEMS_CLASS: {
446: //logger.debug(" < " + tags.getTagById(Fk_pointing_to_items_class));
447: break;
448: }
449:
450: // handle failure:
451: default: {
452: //logger.error("Ignoring unknown Element " + qName);
453: }
454: }
455: } catch (Exception ex) {
456: //logger.error(ex);
457: throw new PersistenceBrokerException(ex);
458: }
459: }
460:
461: public void characters(char ch[], int start, int length) {
462: if (m_CurrentString == null)
463: m_CurrentString = new String(ch, start, length);
464: else
465: m_CurrentString += new String(ch, start, length);
466: }
467:
468: public void error(SAXParseException e) throws SAXException {
469: //logger.error(e);
470: //release the any db connections
471: try {
472: if (m_dBUtility != null)
473: m_dBUtility.release();
474: } catch (Exception ex) {
475: ex.printStackTrace();
476: }
477: throw e;
478: }
479:
480: public void fatalError(SAXParseException e) throws SAXException {
481: //logger.fatal(e);
482: //release the any db connections
483: try {
484: if (m_dBUtility != null)
485: m_dBUtility.release();
486: } catch (Exception ex) {
487: ex.printStackTrace();
488: }
489: throw e;
490: }
491:
492: public void warning(SAXParseException e) throws SAXException {
493: //logger.warn(e);
494: throw e;
495: }
496:
497: /**
498: * maps IsolationLevel literals to the corresponding id
499: * @param isoLevel
500: * @return the id
501: */
502: private int getIsoLevel(String isoLevel) {
503: if (isoLevel.equalsIgnoreCase(LITERAL_IL_READ_UNCOMMITTED)) {
504: return IL_READ_UNCOMMITTED;
505: } else if (isoLevel.equalsIgnoreCase(LITERAL_IL_READ_COMMITTED)) {
506: return IL_READ_COMMITTED;
507: } else if (isoLevel
508: .equalsIgnoreCase(LITERAL_IL_REPEATABLE_READ)) {
509: return IL_REPEATABLE_READ;
510: } else if (isoLevel.equalsIgnoreCase(LITERAL_IL_SERIALIZABLE)) {
511: return IL_SERIALIZABLE;
512: } else if (isoLevel.equalsIgnoreCase(LITERAL_IL_OPTIMISTIC)) {
513: return IL_OPTIMISTIC;
514: }
515: //logger.warn("unknown isolation-level: " + isoLevel + " using RW_UNCOMMITTED as default");
516: return defIsoLevel;
517: }
518:
519: public int getErrorCount() {
520: return m_VerifyExceptions.size();
521: }
522:
523: public int getWarningCount() {
524: return m_VerifyWarnings.size();
525: }
526:
527: private DBUtility getDBUtility(JdbcConnectionDescriptor jcd)
528: throws MetadataException, MalformedURLException,
529: ClassNotFoundException {
530: DBUtility retval = null;
531: String driver;
532: String userName;
533: String password;
534: String url;
535: //if the Tag provided the connection info use that, else
536: //try to connect with the info in the connectionDescriptor
537: if (m_callingTask.hasConnectionInfo()) {
538: m_callingTask
539: .logWarning("Using DB conection info from Ant task.");
540: driver = m_callingTask.getJdbcDriver();
541: userName = m_callingTask.getLogon();
542: password = m_callingTask.getPassword();
543: url = m_callingTask.getUrl();
544: } else {
545: m_callingTask
546: .logWarning("Using DB conection info from ojb repository connection descriptor.");
547: driver = jcd.getDriver();
548: userName = jcd.getUserName();
549: password = jcd.getPassWord();
550: url = jcd.getProtocol() + ":" + jcd.getSubProtocol() + ":"
551: + jcd.getDbAlias();
552: }
553:
554: try {
555: Class jdbcDriver = m_callingTask.loadClass(driver);
556:
557: // not every jdbc driver registers itself with the driver manager
558: // so we do it explicitly
559: DriverManager.registerDriver((Driver) jdbcDriver
560: .newInstance());
561: retval = new DBUtility(url, userName, password);
562: } catch (ClassNotFoundException cnfe) {
563: throw cnfe;
564: } catch (Exception e) {
565: e.printStackTrace();
566: throw new MetadataException(
567: "Could not connect to database with url (" + url
568: + "), driver (" + driver + "), logon ("
569: + userName + "), password (" + password
570: + ").", e);
571: }
572:
573: return retval;
574: }
575:
576: private Constructor m_persistConstructor = null;
577:
578: private Constructor getPersistenceClassConstructor()
579: throws NoSuchMethodException {
580: if (m_persistConstructor == null) {
581: //load the persistent class specified in the OJB.properties file
582: Class persistentClass = m_callingTask
583: .getPersistentFieldClass();
584: Class[] aConTypes = new Class[2];
585: aConTypes[0] = Class.class;
586: aConTypes[1] = String.class;
587: m_persistConstructor = persistentClass
588: .getConstructor(aConTypes);
589: }
590: return m_persistConstructor;
591: }
592:
593: protected void confirmFieldExists(Class classToCheck,
594: String fieldName) throws MetadataException,
595: NoSuchMethodException, InstantiationException,
596: IllegalAccessException {
597: Object[] aConParams = new Object[2];
598: aConParams[0] = classToCheck;
599: aConParams[1] = fieldName;
600:
601: try {
602: getPersistenceClassConstructor().newInstance(aConParams);
603: } catch (InvocationTargetException ite) {
604: throw new MetadataException(ite.getTargetException()
605: .getMessage());
606: }
607: }
608:
609: }
|