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: import java.io.FileInputStream;
018: import java.io.IOException;
019: import java.util.Properties;
020: import javax.xml.parsers.ParserConfigurationException;
021: import javax.xml.parsers.SAXParser;
022: import javax.xml.parsers.SAXParserFactory;
023: import org.apache.ojb.broker.metadata.fieldaccess.PersistentFieldPrivilegedImpl;
024: import org.apache.tools.ant.AntClassLoader;
025: import org.apache.tools.ant.BuildException;
026: import org.apache.tools.ant.Project;
027: import org.apache.tools.ant.Task;
028: import org.apache.tools.ant.types.Path;
029: import org.apache.tools.ant.types.Reference;
030: import org.xml.sax.ContentHandler;
031: import org.xml.sax.SAXException;
032: import org.xml.sax.XMLReader;
033:
034: /**
035: * An Ant task that will read the OJB repository xml file and confirm the
036: * following:
037: *
038: * a)Mapped classes exist. b)Mapped class fields exist c)Mapped database tables
039: * exist. d)Mapped database columns exist. e)Mapped database columns jdbc type
040: * matches the <code><field-descriptor/></code> "jdbc-type" attribute.
041: *
042: * Obviously you should have built your classes, written your OJB
043: * repository.xml file and built your database schema before running this task.
044: *
045: * @author <a href="mailto:daren@softwarearena.com">Daren Drummond</a>
046: * @version $Id: $
047: */
048: public class VerifyMappingsTask extends Task {
049: private int m_errorCount = 0;
050: private int m_warningCount = 0;
051: private String m_repositoryPath = null;
052: private String m_PropertiesFile = null;
053: private String m_jdbcDriver = null;
054: private String m_url = null;
055: private String m_logon = null;
056: private String m_password = null;
057: private boolean m_ignoreCase = false;
058: private boolean m_UseStrictTypeChecking = true;
059: private Class m_persistenceClass = null;
060: private boolean m_useXMLValidation = true;
061: private boolean m_failOnError = true;
062:
063: private Path _classpath;
064:
065: public void setRepositoryFile(String path) {
066: m_repositoryPath = path;
067: }
068:
069: /**
070: * Gets the path to the OJB repository file. (Includes the file name)
071: *
072: * @return The path and file name of the OJB repository file
073: */
074: public String getRepositoryFile() {
075: return m_repositoryPath;
076: }
077:
078: /**
079: * Sets the fully qualified path and file name of the OJB properties file.
080: *
081: * @param path The path and file name of the OJB properties file
082: */
083: public void setPropertiesFile(String path) {
084: m_PropertiesFile = path;
085: }
086:
087: /**
088: * Gets the value set by setPropertiesFile(String path)
089: *
090: * @return The path and file name of the OJB properties file
091: */
092: public String getPropertiesFile() {
093: return m_PropertiesFile;
094: }
095:
096: /**
097: * Sets the fully qualified class name of the jdbc driver to use.
098: *
099: * @param jdbcClass Fully qualified class name of the jdbc driver
100: */
101: public void setJdbcDriver(String jdbcClass) {
102: m_jdbcDriver = jdbcClass;
103: }
104:
105: /**
106: * Gets the value set by setJdbcDriver(String jdbcClass)
107: *
108: * @return Fully qualified class name of the jdbc driver
109: */
110: public String getJdbcDriver() {
111: return m_jdbcDriver;
112: }
113:
114: /**
115: * Sets the url connection string for the jdbc driver.
116: *
117: * @param url The connection string for the jdbc driver
118: */
119: public void setUrl(String url) {
120: m_url = url;
121: }
122:
123: /**
124: * Gets the value set by setUrl(String url)
125: *
126: * @return The connection string for the jdbc driver
127: */
128: public String getUrl() {
129: return m_url;
130: }
131:
132: /**
133: * Sets the database logon account that the utility should use.
134: *
135: * @param logon The database logon account
136: */
137: public void setLogon(String logon) {
138: m_logon = logon;
139: }
140:
141: /**
142: * Gets the value set by setLogon(String logon)
143: *
144: * @return The database logon account
145: */
146: public String getLogon() {
147: return m_logon;
148: }
149:
150: /**
151: * Sets the password for the database logon account.
152: *
153: * @param password The password for the database logon account
154: */
155: public void setPassword(String password) {
156: m_password = password;
157: }
158:
159: /**
160: * Gets the value set by setPassword(String password)
161: *
162: * @return The password for the database logon account
163: */
164: public String getPassword() {
165: return m_password;
166: }
167:
168: /**
169: * Turns on W3C xml validation of the OJB repository.xml file (on by default).
170: *
171: * @param sValidationFlag Whether to validate the xml
172: */
173: public void setUseXMLValidation(String sValidationFlag) {
174: m_useXMLValidation = Boolean.valueOf(sValidationFlag)
175: .booleanValue();
176: }
177:
178: /**
179: * Determines whether the xml syntax is verified.
180: *
181: * @return A flag indicating if W3c xml validation will be used to
182: * verify the OJB repository.xml file
183: */
184: public boolean getUseXMLValidation() {
185: return m_useXMLValidation;
186: }
187:
188: /**
189: * Sets a flag indicating that this Ant task should throw a BuildException
190: * if it encounters any verification errors. In most cases, this will have
191: * the effect of stopping the build process.
192: *
193: * @param sFailFlag Whether to stop the task upon the first error
194: */
195: public void setFailOnError(String sFailFlag) {
196: m_failOnError = Boolean.valueOf(sFailFlag).booleanValue();
197: }
198:
199: /**
200: * Determines whether this task stops by throwing a BuildException when the first
201: * error is encountered.
202: *
203: * @return A flag indicating that the Ant task will throw a
204: * BuildException if it encounters any validation errors
205: */
206: public boolean getFailOnError() {
207: return m_failOnError;
208: }
209:
210: /**
211: * Sets the flag for ignoring the db column name case when looking for db
212: * columns.
213: *
214: * @param sIgnoreCaseFlag Whether the case of the db column name is ignored
215: */
216: public void setIgnoreFieldNameCase(String sIgnoreCaseFlag) {
217: m_ignoreCase = Boolean.valueOf(sIgnoreCaseFlag).booleanValue();
218: }
219:
220: /**
221: * Determines whether the task ignores the case of the db column name case
222: * when looking for db columns.
223: *
224: * @return Flag indicating if the field name case will be
225: * ignored when searching for table column names
226: */
227: public boolean getIgnoreFieldNameCase() {
228: return m_ignoreCase;
229: }
230:
231: /**
232: * Sets the flag for stict type checking of database column types. If this
233: * value is set to "true" then the task will log a warning if the table
234: * column jdbc type doesn't match the type specified in the OJB repository
235: * field descriptor.
236: *
237: * @param sTypeCheckingFlag Whether to use strict type checking
238: */
239: public void setUseStrictTypeChecking(String sTypeCheckingFlag) {
240: m_UseStrictTypeChecking = Boolean.valueOf(sTypeCheckingFlag)
241: .booleanValue();
242: }
243:
244: /**
245: * Gets the boolean equivalent of the value set by
246: * setUseStrictTypeChecking(String sTypeCheckingFlag)
247: *
248: * @return Flag indicating if strict type checking will be
249: * used when searching for database table columns
250: */
251: public boolean getUseStrictTypeChecking() {
252: return m_UseStrictTypeChecking;
253: }
254:
255: /**
256: * Set the classpath for loading the driver.
257: *
258: * @param classpath the classpath
259: */
260: public void setClasspath(Path classpath) {
261: if (_classpath == null) {
262: _classpath = classpath;
263: } else {
264: _classpath.append(classpath);
265: }
266: log("Verification classpath is " + _classpath,
267: Project.MSG_VERBOSE);
268: }
269:
270: /**
271: * Create the classpath for loading the driver.
272: *
273: * @return the classpath
274: */
275: public Path createClasspath() {
276: if (_classpath == null) {
277: _classpath = new Path(getProject());
278: }
279: return _classpath.createPath();
280: }
281:
282: /**
283: * Set the classpath for loading the driver using the classpath reference.
284: *
285: * @param r reference to the classpath
286: */
287: public void setClasspathRef(Reference r) {
288: createClasspath().setRefid(r);
289: log("Verification classpath is " + _classpath,
290: Project.MSG_VERBOSE);
291: }
292:
293: public void execute() throws BuildException {
294: if (getRepositoryFile() == null) {
295: throw new BuildException(
296: "Could not find the repository file.");
297: }
298: try {
299: System.setProperty("OJB.properties", getPropertiesFile());
300: //Thread.currentThread().setContextClassLoader(getClassLoader());
301: logWarning("IgnoreFieldNameCase: "
302: + String.valueOf(getIgnoreFieldNameCase()));
303: logWarning("UseStrictTypeChecking: "
304: + String.valueOf(getUseStrictTypeChecking()));
305: logWarning("UseXMLValidation: "
306: + String.valueOf(getUseXMLValidation()));
307: logWarning("UseStrictTypeChecking: "
308: + String.valueOf(getUseStrictTypeChecking()));
309: verifyRepository(getRepositoryFile());
310: logWarning(getSummaryString(getErrorCount(),
311: getWarningCount()));
312: } catch (Exception e) {
313: logWarning("There was an exception while verifying the repsitory: "
314: + e.getMessage());
315: if (getFailOnError()) {
316: throw new BuildException(
317: "There was an exception while verifying the repsitory.",
318: e);
319: }
320: }
321: if (getFailOnError() && (getErrorCount() > 0)) {
322: throw new BuildException(
323: "Failed because 'failonerror' = true and there are "
324: + String.valueOf(getErrorCount())
325: + " mapping error(s).");
326: }
327: }
328:
329: private String getSummaryString(int iBadCount, int iWarningCount) {
330: return "\n---------------------------------------------------\n Found "
331: + String.valueOf(iBadCount)
332: + " error(s) in the repository. \n Found "
333: + String.valueOf(iWarningCount)
334: + " warning(s) in the repository. \n---------------------------------------------------";
335: }
336:
337: private void verifyRepository(String repositoryFile)
338: throws ParserConfigurationException, SAXException,
339: IOException {
340: log("verifyRepository: Entered.");
341:
342: long start = System.currentTimeMillis();
343: SAXParser p = SAXParserFactory.newInstance().newSAXParser();
344: XMLReader reader = p.getXMLReader();
345:
346: reader.setFeature("http://xml.org/sax/features/validation",
347: getUseXMLValidation());
348:
349: // create handler for verifying the repository structure
350: ContentHandler handler = new RepositoryVerifierHandler(this );
351:
352: logInfo("Starting Parser...");
353: reader.setContentHandler(handler);
354: reader.parse(repositoryFile);
355: logInfo("Done Parsing.");
356:
357: long stop = System.currentTimeMillis();
358:
359: setErrorCount(((RepositoryVerifierHandler) handler)
360: .getErrorCount());
361: setWarningCount(((RepositoryVerifierHandler) handler)
362: .getWarningCount());
363: logWarning("loading XML took " + (stop - start) + " msecs");
364: }
365:
366: /**
367: * Log a warning with the Ant out stream.
368: *
369: * @param msg The message to log
370: */
371: public void logWarning(String msg) {
372: log(msg, Project.MSG_WARN);
373: }
374:
375: /**
376: * Log an Info message with the Ant out stream. Info messages can be
377: * suppressed from the command line by starting ant with the -quiet option.
378: *
379: * @param msg The message to log
380: */
381: public void logInfo(String msg) {
382: log(msg, Project.MSG_INFO);
383: }
384:
385: protected int getErrorCount() {
386: return m_errorCount;
387: }
388:
389: protected void setErrorCount(int count) {
390: m_errorCount = count;
391: }
392:
393: protected int getWarningCount() {
394: return m_warningCount;
395: }
396:
397: protected void setWarningCount(int count) {
398: m_warningCount = count;
399: }
400:
401: /**
402: * Tests to see if the jdbc connection information was specified in the tag
403: * xml.
404: *
405: * @return <code>true</code> if the jdbc connection information was
406: * supplied in the tag xml
407: */
408: public boolean hasConnectionInfo() {
409: return (m_jdbcDriver != null && m_url != null
410: && m_logon != null && m_password != null);
411: }
412:
413: Class loadClass(String className) throws ClassNotFoundException {
414: if (_classpath != null) {
415: log("Loading " + className
416: + " using AntClassLoader with classpath "
417: + _classpath, Project.MSG_VERBOSE);
418:
419: AntClassLoader loader = new AntClassLoader(getProject(),
420: _classpath);
421:
422: return loader.loadClass(className);
423: } else {
424: log("Loading " + className + " using system loader.",
425: Project.MSG_VERBOSE);
426: return Class.forName(className);
427: }
428: }
429:
430: /**
431: * Returns the Class object of the class specified in the OJB.properties
432: * file for the "PersistentFieldClass" property.
433: *
434: * @return Class The Class object of the "PersistentFieldClass" class
435: * specified in the OJB.properties file.
436: */
437: public Class getPersistentFieldClass() {
438: if (m_persistenceClass == null) {
439: Properties properties = new Properties();
440: try {
441: this .logWarning("Loading properties file: "
442: + getPropertiesFile());
443: properties
444: .load(new FileInputStream(getPropertiesFile()));
445: } catch (IOException e) {
446: this .logWarning("Could not load properties file '"
447: + getPropertiesFile()
448: + "'. Using PersistentFieldDefaultImpl.");
449: e.printStackTrace();
450: }
451: try {
452: String className = properties
453: .getProperty("PersistentFieldClass");
454:
455: m_persistenceClass = loadClass(className);
456: } catch (ClassNotFoundException e) {
457: e.printStackTrace();
458: m_persistenceClass = PersistentFieldPrivilegedImpl.class;
459: }
460: logWarning("PersistentFieldClass: "
461: + m_persistenceClass.toString());
462: }
463: return m_persistenceClass;
464: }
465: }
|