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