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