001 /** 002 * Copyright 2004-2013 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.apache.torque.mojo; 017 018 import java.io.File; 019 import java.io.FileInputStream; 020 import java.io.IOException; 021 import java.io.InputStream; 022 import java.io.OutputStream; 023 import java.util.HashSet; 024 import java.util.List; 025 import java.util.Properties; 026 import java.util.Set; 027 import java.util.TreeSet; 028 029 import org.apache.commons.io.FileUtils; 030 import org.apache.commons.io.IOUtils; 031 import org.apache.maven.plugin.MojoExecutionException; 032 import org.apache.maven.plugin.MojoFailureException; 033 import org.apache.tools.ant.DirectoryScanner; 034 import org.apache.torque.engine.database.model.Database; 035 import org.apache.torque.engine.database.model.Table; 036 import org.kuali.core.db.torque.SetUtils; 037 import org.kuali.core.db.torque.Utils; 038 039 /** 040 * This mojo identifies data files that are present on the file system but are not present in schema.xml. This can 041 * happen if a table is removed from the schema. 042 * 043 * It sets a project property called "impex.data.invalid". This property is a comma delimited list of filenames that 044 * have no match in the db schema. 045 * 046 * If it finds any invalid files it will also set the project property "impex.found.invalid=true" 047 * 048 * @goal id-invalid-data-files 049 */ 050 public class IdentifyInvalidDataFiles extends BaseMojo { 051 private static final String FS = System.getProperty("file.separator"); 052 053 /** 054 * @parameter expression="${extension}" default-value=".xml" 055 * @required 056 */ 057 private String extension; 058 059 /** 060 * @parameter expression="${dataDir}" default-value="${project.basedir}/src/main/impex" 061 * @required 062 */ 063 private File dataDir; 064 065 /** 066 * @parameter expression="${dataDirIncludes}" default-value="*.xml" 067 */ 068 private String dataDirIncludes; 069 070 /** 071 * @parameter expression="${dataDirExcludes}" default-value="schema.xml" 072 */ 073 private String dataDirExcludes; 074 075 /** 076 * @parameter expression="${schemaXMLFile}" default-value="src/main/impex/schema.xml" 077 */ 078 private String schemaXMLFile; 079 080 /** 081 * @parameter expression="${targetDatabase}" default-value="oracle" 082 */ 083 private String targetDatabase; 084 085 /** 086 * Any invalid files are listed in this file. One file per line 087 * 088 * @parameter expression="${impex.markedForRemoval}" default-value="${project.build.directory}/impex/invalid.txt" 089 */ 090 private File markedForRemoval; 091 092 protected Set<File> getExportedFiles() throws MojoExecutionException { 093 String filename = getProject().getBuild().getDirectory() + "/impex/exported-tables.txt"; 094 File file = new File(filename); 095 if (!file.exists()) { 096 getLog().warn("Could not locate " + file.getAbsolutePath()); 097 return new HashSet<File>(); 098 } 099 List<String> tablenames = getContents(file); 100 return getExportedFiles(tablenames); 101 } 102 103 protected Set<File> getExportedFiles(List<String> tablenames) { 104 Set<File> files = new TreeSet<File>(); 105 for (String tablename : tablenames) { 106 String filename = dataDir.getAbsolutePath() + FS + tablename + extension; 107 File file = new File(filename); 108 files.add(file); 109 } 110 return files; 111 } 112 113 @SuppressWarnings("unchecked") 114 protected List<String> getContents(File file) throws MojoExecutionException { 115 InputStream in = null; 116 try { 117 in = new FileInputStream(file); 118 return IOUtils.readLines(in); 119 } catch (IOException e) { 120 throw new MojoExecutionException("Unexpected error", e); 121 } finally { 122 IOUtils.closeQuietly(in); 123 } 124 } 125 126 @Override 127 protected void executeMojo() throws MojoExecutionException, MojoFailureException { 128 Utils utils = new Utils(); 129 try { 130 getLog().info("Examining " + dataDir.getAbsolutePath()); 131 Database db = utils.getDatabase(schemaXMLFile, targetDatabase); 132 DirectoryScanner ds = getDirectoryScanner(); 133 Set<File> existing = getExistingFiles(ds); 134 Set<File> exported = getExportedFiles(); 135 Set<File> allowed = getDatabaseFiles(db); 136 if (exported.size() > 0) { 137 allowed = exported; 138 } 139 Set<File> invalid = SetUtils.difference(existing, allowed); 140 getLog().info(existing.size() + " data files currently exist"); 141 getLog().info(invalid.size() + " of those are invalid"); 142 StringBuilder sb = new StringBuilder(); 143 int count = 0; 144 StringBuilder invalidFiles = new StringBuilder(); 145 for (File file : invalid) { 146 if (count != 0) { 147 sb.append(","); 148 } 149 sb.append("**/src/main/impex/" + file.getName()); 150 invalidFiles.append(file.getCanonicalPath() + "\n"); 151 getLog().info("Marked for removal: " + file.getName()); 152 count++; 153 } 154 Properties properties = getProject().getProperties(); 155 properties.setProperty("impex.data.invalid", sb.toString()); 156 if (count > 0) { 157 properties.setProperty("impex.found.invalid", Boolean.TRUE.toString()); 158 createFile(markedForRemoval, invalidFiles.toString()); 159 } 160 } catch (Exception e) { 161 throw new MojoExecutionException("Error executing mojo", e); 162 } 163 } 164 165 protected void createFile(File file, String contents) throws IOException { 166 OutputStream out = null; 167 try { 168 out = FileUtils.openOutputStream(file); 169 IOUtils.write(contents, out); 170 } finally { 171 IOUtils.closeQuietly(out); 172 } 173 } 174 175 protected Set<File> getDatabaseFiles(Database db) { 176 List<?> tables = db.getTables(); 177 Set<File> files = new TreeSet<File>(); 178 for (Object object : tables) { 179 Table table = (Table) object; 180 String tableName = table.getName(); 181 String filename = dataDir.getAbsolutePath() + FS + tableName + extension; 182 File file = new File(filename); 183 files.add(file); 184 } 185 return files; 186 } 187 188 protected Set<File> getExistingFiles(DirectoryScanner ds) { 189 ds.scan(); 190 String[] relativeFilenames = ds.getIncludedFiles(); 191 Set<File> files = new TreeSet<File>(); 192 for (int i = 0; i < relativeFilenames.length; i++) { 193 String filename = ds.getBasedir().getAbsolutePath() + FS + relativeFilenames[i]; 194 File file = new File(filename); 195 files.add(file); 196 } 197 return files; 198 } 199 200 protected DirectoryScanner getDirectoryScanner() { 201 DirectoryScanner ds = new DirectoryScanner(); 202 ds.setBasedir(dataDir); 203 ds.setIncludes(new String[] { dataDirIncludes }); 204 ds.setExcludes(new String[] { dataDirExcludes }); 205 return ds; 206 } 207 208 protected File getFile(Table table) { 209 String tableName = table.getName(); 210 String filename = dataDir.getAbsolutePath() + FS + tableName + extension; 211 File file = new File(filename); 212 return file; 213 } 214 215 public File getDataDir() { 216 return dataDir; 217 } 218 219 public void setDataDir(File dataDir) { 220 this.dataDir = dataDir; 221 } 222 223 public String getDataDirIncludes() { 224 return dataDirIncludes; 225 } 226 227 public void setDataDirIncludes(String dataDirIncludes) { 228 this.dataDirIncludes = dataDirIncludes; 229 } 230 231 public String getDataDirExcludes() { 232 return dataDirExcludes; 233 } 234 235 public void setDataDirExcludes(String dataDirExcludes) { 236 this.dataDirExcludes = dataDirExcludes; 237 } 238 239 public String getSchemaXMLFile() { 240 return schemaXMLFile; 241 } 242 243 public void setSchemaXMLFile(String schemaXMLFile) { 244 this.schemaXMLFile = schemaXMLFile; 245 } 246 247 public String getExtension() { 248 return extension; 249 } 250 251 public void setExtension(String extension) { 252 this.extension = extension; 253 } 254 255 public String getTargetDatabase() { 256 return targetDatabase; 257 } 258 259 public void setTargetDatabase(String targetDatabase) { 260 this.targetDatabase = targetDatabase; 261 } 262 263 public File getMarkedForRemoval() { 264 return markedForRemoval; 265 } 266 267 public void setMarkedForRemoval(File markedForRemoval) { 268 this.markedForRemoval = markedForRemoval; 269 } 270 271 }