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    }