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