001 /**
002 * Copyright 2004-2012 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 }